❤️ Spring
代碼:https://github.com/Donkequan/Spring-Study
❤️ 希望各位博友三連+關注!!!
1、簡介
spring理念:是現有的技術更加容易使用,本身是一個大雜燴。
- SSH:Struct2 + Spring + Hibernate
- SSM: SpringMVC + Spring + Mybatis
官網: https://spring.io/projects/spring-framework#overview
官方下載下傳: https://repo.spring.io/release/org/springframework/spring/
GitHub: https://github.com/spring-projects/spring-framework
1.1、導入包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
1.2、優點
- spring是開源的免費的容器。
- spring是一個輕量級的,非入侵式的。
- 控制反轉(IOC),面向切面程式設計 (AOP)。
- 支援事務處理,對架構整合的支援。
總結:spring是一個輕量級的控制反轉(IOC)和面向切面程式設計(AOP)的架構。
1.3、Spring組成
1.4、拓展
- Spring Boot 建構一切
- 一個快速開發的腳手架
- 基于SpringBoot可以快速的開發單個微服務
- 限制大于配置!
- Spring Cloud 協調一切
- SpringCloud是基于SpringBoot實作的
- Spring Cloud Data Flow 連接配接一切
學習SpringBoot的前提,需要完全掌握Spring以及SpringMVC!
2、IOC理論推導
- UserDao
- UserDaoImp
- UserSevice
- UserServiceImp
在之前,使用者的需求可能會影響原來的代碼。
2.1、分析實作
建立一個空白的maven項目
分析實作
我們先用我們原來的方式寫一段代碼 .
1、先寫一個UserDao接口
public interface UserDao {
public void getUser();
}
2、再去寫Dao的實作類
public class UserDaoImp implements UserDao{
@Override
public void getUser() {
System.out.println("預設擷取使用者的資料");
}
}
3、然後去寫UserService的接口
public interface UserService {
public void getUser();
}
4、最後寫Service的實作類
public class UserServiceImp implements UserService{
private UserDao userDao = new UserDaoImp();
public void getUser(){
userDao.getUser();
}
}
5、測試一下
@Test
public void MyTest(){
UserService service = new UserServiceImpl();
service.getUser();
}
這是我們原來的方式 , 開始大家也都是這麼去寫的對吧 . 那我們現在修改一下 .
把Userdao的實作類增加一個 .
public class UserDaoMysqlImp implements UserDao{
@Override
public void getUser() {
System.out.println("Mysql擷取使用者資料!");
}
}
緊接着我們要去使用MySql的話 , 我們就需要去service實作類裡面修改對應的實作
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoMySqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
在假設, 我們再增加一個Userdao的實作類 .
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle擷取使用者資料");
}
}
那麼我們要使用Oracle , 又需要去service實作類裡面修改對應的實作 . 假設我們的這種需求非常大 , 這種方式就根本不适用了, 甚至反人類對吧 , 每次變動 , 都需要修改大量代碼 . 這種設計的耦合性太高了, 牽一發而動全身 .
那我們如何去解決呢 ?
我們可以在需要用到他的地方 , 不去實作它 , 而是留出一個接口 , 利用set , 我們去代碼裡修改下 .
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set實作
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
現在去我們的測試類裡 , 進行測試 ;
public class MyTest {
public static void main(String[] args) {
//使用者實際調用的是業務層,dao層他們不需要接觸!
UserServiceImp userService = new UserServiceImp();
((UserServiceImp) userService).setUserDao(new UserDaoSqlserviceImp());
userService.getUser();
}
}
使用一個set。
private UserDao userDao;
//利用set進行動态實作值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
- 之前是主動建立對象,控制權在程式猿手上!
- 使用set之後,程式不再具有主動性,而是變成了被動接受對象。
這種思想,從本質上解決了問題,我們程式猿不用再去管理對象的建立。系統的耦合性大大降低,可以更加專注在業務的實作上!這是IOC的原型!
2.2、IOC本質
控制反轉IoC(Inversion of Control),是一種設計思想,**DI(依賴注入)是實作IoC的一種方法,**也有人認為DI隻是IoC的另一種說法。沒有IoC的程式中 , 我們使用面向對象程式設計 , 對象的建立與對象間的依賴關系完全寫死在程式中,對象的建立由程式自己控制,控制反轉後将對象的建立轉移給第三方,個人認為所謂控制反轉就是:獲得依賴對象的方式反轉了。
IoC是Spring架構的核心内容,使用多種方式完美的實作了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置實作IoC。
Spring容器在初始化時先讀取配置檔案,根據配置檔案或中繼資料建立與組織對象存入容器中,程式使用時再從Ioc容器中取出需要的對象。
采用XML方式配置Bean的時候,Bean的定義資訊是和實作分離的,而采用注解的方式可以把兩者合為一體,Bean的定義資訊直接以注解的形式定義在實作類中,進而達到了零配置的目的。
控制反轉是一種通過描述(XML或注解)并通過第三方去生産或擷取特定對象的方式。在Spring中實作控制反轉的是IoC容器,其實作方法是依賴注入(Dependency Injection,DI)。
示例:
HelloSpring
1、導入Jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
2、編寫一個Hello實體類
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
3、編寫我們的spring檔案 , 這裡我們命名為beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用Spring來建立對象,在Spring中這些都稱為Bean
類型 變量名 = new 類型
Hello hello = new Hello();
bean=對象 new Hello()
id=變量名
class=new 的對象
property 相當于給對象中的屬性設定有一個值
-->
<bean id="hello" class="com.kk.pojo.Hello">
<property name="str" value="Spring">
</property>
</bean>
</beans>
4、測試
import com.kk.pojo.Hello;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//擷取Spring的上下文對象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我們的對象現在都在Spring中管理了,我們要使用,直接去裡面取出來就可以了
Hello hello = (Hello)context.getBean("hello");
hello.show();
// System.out.println(hello.toString());
}
}
思考
- Hello 對象是誰建立的 ? 【hello 對象是由Spring建立的
- Hello 對象的屬性是怎麼設定的 ? hello 對象的屬性是由Spring容器設定的
這個過程就叫控制反轉 :
- 控制 : 誰來控制對象的建立 , 傳統應用程式的對象是由程式本身控制建立的 , 使用Spring後 , 對象是由Spring來建立的
- 反轉 : 程式本身不建立對象 , 而變成被動的接收對象 .
依賴注入 : 就是利用set方法來進行注入的.
IOC是一種程式設計思想,由主動的程式設計變成被動的接收
可以通過newClassPathXmlApplicationContext去浏覽一下底層源碼 .
修改案例一
我們在案例一中, 新增一個Spring配置檔案beans.xml
注入:
1.ref:引用Spring容器中建立好的對象
2.value:具體的值,基本資料類型
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="mysqlImp" class="com.kk.dao.UserDaoMysqlImp"></bean>
<bean id="oracleImp" class="com.kk.dao.UserDaoOracleImp"></bean>
<bean id="SqlserviceImp" class="com.kk.dao.UserDaoSqlserviceImp"></bean>
<bean id="UserServiceImp" class="com.kk.service.UserServiceImp">
<property name="userDao" ref="SqlserviceImp"></property>
</bean>
<!-- 注入-->
<!-- ref:引用Spring容器中建立好的對象
value:具體的值,基本資料類型-->
</beans>
測試:
import com.kk.dao.UserDaoImp;
import com.kk.dao.UserDaoSqlserviceImp;
import com.kk.service.UserServiceImp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//擷取ApplicationContext 拿到Spring的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImp userServiceImp = (UserServiceImp)context.getBean("UserServiceImp");
userServiceImp.getUser();
}
}
2.3、IOC建立對象方式
-
使用無參構造建立對象,預設。
實體類
package com.kk.pojo;
public class User {
private String name;
public User() {
System.out.println("user無參構造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
2.beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="user" class="com.kk.pojo.User">
<!-- 有一個屬性name需要注入,-->
<property name="name" value="KK"></property>
</bean>
</beans>
3、測試類
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User)context.getBean("user");
user.show();
}
}
- 使用有參構造
-
下标指派
實體類
package com.kk.pojo;
public class User {
private String name;
public User(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="user" class="com.kk.pojo.User">
<!-- 第一種:下标指派-->
<constructor-arg index="0" value="kkkkkk"></constructor-arg>
</bean>
</beans>
測試
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User)context.getBean("user");
user.show();
}
}
2.通過類型建立
<!-- 第二種方式:通過類型建立 不建議使用以下方法-->
<bean id="user" class="com.kk.pojo.User">
<constructor-arg type="java.lang.String" value="k11"></constructor-arg>
</bean>
3.直接通過參數名建立
<!-- 第三種方式:直接通過參數名建立-->
<bean id="user" class="com.kk.pojo.User">
<constructor-arg name="name" value="k111"></constructor-arg>
</bean>
3、 Spring配置
3.1、别名
<!-- 别名,如果添加了别名,我們也可以通過别名擷取-->
<alias name="user" alias="abc"></alias>
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User)context.getBean("abc");
user.show();
}
}
3.2、Bean的配置
- id:bean的id辨別符
- class:bean對象所對應的類型
- name:别名,更進階,可以同時取多個别名。
<!-- id:bean的唯一辨別符,也是相當于我們學的對象名
class:bean對象所對應的權限定名:包名+類型
name :也是别名 而且name可以取多個别名
-->
<bean id="userT" class="com.kk.pojo.UserT" name="user2,u2 u3;u4">
<property name="name" value="KK"></property>
</bean>
3.3、import
這個import,一般用于團隊開發使用,可以将多個配置檔案,導入合并為一個
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="beans.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>
</beans>
4、DI依賴注入
4.1、構造器注入
4.2、Set方式注入
- 依賴注入:Set注入
- 依賴:bean對象的建立依賴于容器
- 注入,bean對象中的所有屬性由容器來注入
環境搭建
1.複雜類型
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2.真實測試對象
package com.kk.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public Student() {
}
public Student(String name, Address address, String[] books,
List<String> hobbys, Map<String, String> card,
Set<String> games, String wife, Properties info) {
this.name = name;
this.address = address;
this.books = books;
this.hobbys = hobbys;
this.card = card;
this.games = games;
this.wife = wife;
this.info = info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
3.bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 第一種:普通值注入 使用value值注入-->
<bean id="student" class="com.kk.pojo.Student">
<property name="name" value="火神"></property>
</bean>
</beans>
4.測試類:
import com.kk.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student =(Student) context.getBean("student");
System.out.println(student.getName());
}
}
5.完善注入資訊:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="address" class="com.kk.pojo.Address">
<property name="address" value="廣東"></property>
</bean>
<bean id="student" class="com.kk.pojo.Student">
<!-- 第一種:普通值注入 使用value值注入-->
<property name="name" value="火神"></property>
<!-- 第二種:Bean注入 ,使用ref注入-->
<property name="address" ref="address"></property>
<!-- 數組注入,-->
<property name="books">
<array>
<value>紅樓夢</value>
<value>西遊記</value>
<value>三國演義</value>
<value>水浒傳</value>
</array>
</property>
<!-- list注入-->
<property name="hobbys">
<list>
<value>程式設計</value>
<value>聽歌</value>
<value>吃</value>
</list>
</property>
<!-- map注入-->
<property name="card">
<map>
<entry key="身份證" value="4412002121223***"></entry>
<entry key="銀行卡" value="654645454546"></entry>
</map>
</property>
<!-- set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>CSOL</value>
</set>
</property>
<!-- wife注入-->
<property name="wife">
<null></null>
</property>
<!-- Properties-->
<property name="info">
<props>
<prop key="學号">2011***</prop>
<prop key="性别">男</prop>
<prop key="姓名">k</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
4.3、其他方式注入
實體類:
package com.kk.pojo;
public class User {
private int age;
private String name;
//c命名空間需要有參構造器
public User(int age, String name) {
this.age = age;
this.name = name;
}
public User() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
1、P命名空間注入 : 需要在頭檔案中加入限制檔案
<!--P(屬性: properties)命名空間 , 屬性依然要設定set方法-->
<bean id="user" class="com.kuang.pojo.User" p:name="kk" p:age="17"/>
測試類:
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
// User user =(User) context.getBean("user");
User user = context.getBean("user", User.class);
System.out.println(user);
}
2、c 命名空間注入 : 需要在頭檔案中加入限制檔案
<!--C(構造: Constructor)命名空間 , 屬性依然要設定set方法-->
<bean id="user" class="com.kuang.pojo.User" c:name="kk" c:age="17"/>
測試類:
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
System.out.println(user);
}
4.4、bean的作用域
在Spring中,那些組成應用程式的主體及由Spring IOC容器所管理的對象,被稱之為bean。簡單地講,bean就是由IoC容器初始化、裝配及管理的對象
幾種作用域中,request、session作用域僅在基于web的應用中使用(不必關心你所采用的是什麼web應用架構),隻能用在基于web的Spring ApplicationContext環境。
4.4.1、Singleton單例模式(Spring預設機制)
當一個bean的作用域為Singleton,那麼Spring IOC容器中隻會存在一個共享的bean執行個體,并且所有對bean的請求,隻要id與該bean定義相比對,則隻會傳回bean的同一執行個體。Singleton是單例類型,就是在建立起容器時就同時自動建立了一個bean的對象,不管你是否使用,他都存在了,每次擷取到的對象都是同一個對象。注意,Singleton作用域是Spring中的預設作用域。要在XML中将bean定義成singleton,可以這樣配置:
<bean id="user2" class="com.kk.pojo.User" c:name="kk" c:age="99" scope="singleton"></bean>
測試:
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user==user2);//true
System.out.println(user);
}
4.4.2、Prototype(原型模式)每次get的時候都會産生新的對象
當一個bean的作用域為Prototype,表示一個bean定義對應多個對象執行個體。Prototype作用域的bean會導緻在每次對該bean請求(将其注入到另一個bean中,或者以程式的方式調用容器的getBean()方法)時都會建立一個新的bean執行個體。Prototype是原型類型,它在我們建立容器的時候并沒有執行個體化,而是當我們擷取bean的時候才會去建立一個對象,而且我們每次擷取到的對象都不是同一個對象。根據經驗,對有狀态的bean應該使用prototype作用域,而對無狀态的bean則應該使用singleton作用域。在XML中将bean定義成prototype,可以這樣配置:
<!-- 每次get的時候都會産生新的對象-->
<bean id="user2" class="com.kk.pojo.User" c:name="kk" c:age="99" scope="prototype"></bean>
測試:
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user.hashCode());
System.out.println(user2.hashCode());
//兩個值不同
System.out.println(user==user2);//false
System.out.println(user);
}
4.4.3、Request
當一個bean的作用域為Request,表示在一次HTTP請求中,一個bean定義對應一個執行個體;即每個HTTP請求都會有各自的bean執行個體,它們依據某個bean定義建立而成。該作用域僅在基于web的Spring ApplicationContext情形下有效。考慮下面bean定義:
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
針對每次HTTP請求,Spring容器會根據loginAction bean的定義建立一個全新的LoginAction bean執行個體,且該loginAction bean執行個體僅在目前HTTP request内有效,是以可以根據需要放心的更改所建執行個體的内部狀态,而其他請求中根據loginAction bean定義建立的執行個體,将不會看到這些特定于某個請求的狀态變化。當處理請求結束,request作用域的bean執行個體将被銷毀。
4.4.4、Session
當一個bean的作用域為Session,表示在一個HTTP Session中,一個bean定義對應一個執行個體。該作用域僅在基于web的Spring ApplicationContext情形下有效。考慮下面bean定義:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
針對某個HTTP Session,Spring容器會根據userPreferences bean定義建立一個全新的userPreferences bean執行個體,且該userPreferences bean僅在目前HTTP Session内有效。與request作用域一樣,可以根據需要放心的更改所建立執行個體的内部狀态,而别的HTTP Session中根據userPreferences建立的執行個體,将不會看到這些特定于某個HTTP Session的狀态變化。當HTTP Session最終被廢棄的時候,在該HTTP Session作用域内的bean也會被廢棄掉。
5、Bean的自動裝配
- 自動裝配是Spring滿足bean依賴的一種方式!
- Spring會在上下文中自動尋找,并自動給bean裝配屬性!
在Spring中有三種自動裝配的方式
- 在xml中顯示的配置
- 在java中顯示配置
- 隐式的自動裝配bean(重要)
5.1、測試
環境搭建:一個人有兩個寵物!
實體類:
Cat
package com.kk.pojo;
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
Dog
package com.kk.pojo;
public class Dog {
public void shout(){
System.out.println("Wang~");
}
}
People
package com.kk.pojo;
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
beans.xml:
5.2、ByName自動裝配
byName:會自動在容器上下文中查找,和自己對象set方法後面的值對應的bean!
<bean id="cat" class="com.kk.pojo.Cat"></bean>
<bean id="dog" class="com.kk.pojo.Dog"></bean>
<!-- byName:會自動在容器上下文中查找,和自己對象set方法後面的值對應的bean!-->
<bean id="people" class="com.kk.pojo.People" autowire="byName">
<property name="name" value="kk"></property>
<!-- <property name="dog" value="dog"></property>-->
<!-- <property name="cat" value="cat"></property>-->
</bean>
5.3、ByType自動裝配
byType:會自動在容器上下文中查找,和自己對象屬性類型相同的bean! 必須保證類目全局唯一
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="cat" class="com.kk.pojo.Cat"></bean>
<!-- <bean id="dog1212" class="com.kk.pojo.Dog"></bean>-->
<bean class="com.kk.pojo.Dog"></bean>
<!-- byType:會自動在容器上下文中查找,和自己對象屬性類型相同的bean! 必須保證類目全局唯一-->
<bean id="people" class="com.kk.pojo.People" autowire="byType">
<property name="name" value="kk"></property>
<!-- <property name="dog" value="dog"></property>-->
<!-- <property name="cat" value="cat"></property>-->
</bean>
</beans>
測試類:
import com.kk.pojo.People;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getDog().shout();
people.getCat().shout();
}
}
小結
- ByName的時候,需要保證所有bean的id唯一,并且這個bean需要和自動注入的屬性的set方法的值一緻!
- ByType的時候,需要保證所有bean的class唯一,并且這個bean需要和自動注入的屬性的類型一緻!
5.4、使用注解實作自動裝配
使用注解
jdk1.5開始支援注解,spring2.5開始全面支援注解。
準備工作:利用注解的方式注入屬性。
1、在spring配置檔案中引入context檔案頭
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
2、開啟屬性注解支援!
<!-- 開啟注解的支援-->
<context:annotation-config/>
5.4.1、Autowired
直接在屬性上使用即可!也可以在set方式上使用!
使用Autowired我們可以不用編寫set方法,前提是這個自動裝配的屬性在IOC(Spring)容器中存在,且符合名字ByName!
測試:
實體類
package com.kk.pojo;
public class cat {
public void shout(){
System.out.println("miao~");
}
}
package com.kk.pojo;
public class dog {
public void shout(){
System.out.println("Wang~");
}
}
package com.kk.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class People {
@Autowired
private cat cat;
@Autowired
private dog dog;
private String name;
public com.kk.pojo.cat getCat() {
return cat;
}
public void setCat(com.kk.pojo.cat cat) {
this.cat = cat;
}
public com.kk.pojo.dog getDog() {
return dog;
}
public void setDog(com.kk.pojo.dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 開啟注解的支援-->
<context:annotation-config/>
<bean id="cat" class="com.kk.pojo.cat"></bean>
<bean id="dog" class="com.kk.pojo.dog"></bean>
<bean id="people" class="com.kk.pojo.People"></bean>
</beans>
測試類:
import com.kk.pojo.People;
import org.testng.annotations.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getCat().shout();
people.getDog().shout();
}
}
注意:
@Nullable 字段标記了這個
public @interface Autowired{
booleam required() default true;
}
public class People {
//如果顯示定義了Autowired的required的屬性為false,說明這個對象可以為null,否則不允許為空
@Autowired(required = false)
private cat cat;
@Autowired
private dog dog;
private String name;
}
@Qualifier
如果@Autowired自動裝配的環境比較複雜,自動裝配無法通過一個注解@Autowired完成的時候,我們可以使用@Qualifier(value=“xxxx”)去配置@Autowired的使用,指定一個唯一的bean對象注入!
- @Autowired是根據類型自動裝配的,加上@Qualifier則可以根據byName的方式自動裝配
- @Qualifier不能單獨使用。
測試實驗步驟:
1、配置檔案修改内容,保證類型存在對象。且名字不為類的預設名字!
<bean id="dog1" class="com.kk.pojo.dog"/>
<bean id="dog2" class="com.kk.pojo.dog"/>
<bean id="cat1" class="com.kk.pojo.cat"/>
<bean id="cat2" class="com.kk.pojo.cat"/>
2、沒有加Qualifier測試,直接報錯
3、在屬性上添加Qualifier注解
@Autowired
@Qualifier(value = "cat2")
private cat cat;
@Autowired
@Qualifier(value = "dog2")
private dog dog;
測試,成功輸出!
@Resource
- @Resource如有指定的name屬性,先按該屬性進行byName方式查找裝配;
- 其次再進行預設的byName方式進行裝配;
- 如果以上都不成功,則按byType的方式自動裝配。
- 都不成功,則報異常。
實體類:
public class People {
//如果顯示定義了Autowired的required的屬性為false,說明這個對象可以為null,否則不允許為空
// @Autowired(required = false)
@Resource(name = "cat2")
private cat cat;
// @Autowired
// @Qualifier(value="dog1")
@Resource
private dog dog;
private String name;
}
beans.xml
<!-- 開啟注解的支援-->
<context:annotation-config/>
<bean id="cat1" class="com.kk.pojo.cat"></bean>
<bean id="cat2" class="com.kk.pojo.cat"></bean>
<bean id="dog" class="com.kk.pojo.dog"></bean>
<bean id="dog11" class="com.kk.pojo.dog"></bean>
<bean id="people" class="com.kk.pojo.People"></bean>
測試:結果OK
配置檔案2:beans.xml , 删掉cat2
<bean id="dog" class="com.kk.pojo.dog"></bean>
<bean id="cat1" class="com.kk.pojo.cat"></bean>
實體類上隻保留注解
@Resource
private cat cat;
@Resource
private dog dog;
結果:OK
結論:先進行byName查找,失敗;再進行byType查找,成功。
小結
@Autowired與@Resource異同:
1、@Autowired與@Resource都可以用來裝配bean。都可以寫在字段上,或寫在setter方法上。
2、@Autowired預設按類型裝配(屬于spring規範),預設情況下必須要求依賴對象必須存在,如果要允許null 值,可以設定它的required屬性為false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier注解進行使用
3、@Resource(屬于J2EE複返),預設按照名稱進行裝配,名稱可以通過name屬性進行指定。如果沒有指定name屬性,當注解寫在字段上時,預設取字段名進行按照名稱查找,如果注解寫在setter方法上預設取屬性名進行裝配。當找不到與名稱比對的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就隻會按照名稱進行裝配。
它們的作用相同都是用注解方式注入對象,但執行順序不同。@Autowired先byType,@Resource先byName。
6、使用注解開發
在Spring4之後,要使用注解開發,必須保證aop的包導入了
使用注解開發需要導入context限制,增加注解的支援!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 開啟注解的支援-->
<context:annotation-config/>
</beans>
6.1、bean
我們之前都是使用 bean 的标簽進行bean注入,但是實際開發中,我們一般都會使用注解!
1、配置掃描哪些包下的注解
<!--指定注解掃描包-->
<context:component-scan base-package="com.kk.pojo"/>
2、在指定包下編寫類,增加注解
package com.kk.pojo;
import org.springframework.stereotype.Component;
//這裡等價于 <bean id="user" class="com.kk.pojo.User"></bean>
//Component 元件
@Component
public class User {
public String name="k";
}
3、測試
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// User user = (User)context.getBean("user");
User user = context.getBean("user", User.class);
System.out.println(user.name);
}
}
6.2、屬性如何注入
package com.kk.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//這裡等價于 <bean id="user" class="com.kk.pojo.User"></bean>
//Component 元件
@Component
public class User {
public String name;
// 相當于 <property name="name" value="kk"></property>
@Value("kk")
public void setName(String name) {
this.name = name;
}
}
6.3、衍生的注解
@Component有幾個衍生注解,我們在web開發中,會按照MVC三層架構分層!
- dao【@Repository】
- service【@Service】
- controller【@Controller】
這四個注解的功能都是一樣的,都是代表将某個類注冊到Spring中,裝配Bean
6.4、自動裝配置
-@Autowired :自動裝配通過類型 名字
如果Autowired不能唯一自動裝配上屬性,則需要通過@Qualifier(value="xxxx")
-@Nullable 字段标記了這個注解,說明這個字段可以為null
-Resource:自動裝配通過名字 類型
6.5、作用域
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 指定要掃描的包,這個包下的注解就會生效-->
<context:component-scan base-package="com.kk"></context:component-scan>
<!-- 開啟注解的支援-->
<context:annotation-config/>
<!-- <bean id="user" class="com.kk.pojo.User">-->
<!-- <property name="name" value="kk"></property>-->
<!-- </bean>-->
</beans>
package com.kk.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//這裡等價于 <bean id="user" class="com.kk.pojo.User"></bean>
//Component 元件
@Component
@Scope("prototype")
public class User {
public String name;
// 相當于 <property name="name" value="kk"></property>
@Value("kk")
public void setName(String name) {
this.name = name;
}
}
/*
@scope
singleton:預設的,Spring會采用單例模式建立這個對象。關閉工廠 ,所有的對象都會銷毀。
prototype:多例模式。關閉工廠 ,所有的對象不會銷毀。内部的垃圾回收機制會回收
*/
6.6、小結
XML與注解比較
- XML可以适用任何場景 ,結構清晰,維護友善
- 注解不是自己提供的類使用不了,開發簡單友善,維護相對複雜
xml與注解整合開發 :推薦最佳實踐
- xml管理Bean
- 注解隻負責完成屬性注入
- 我們在使用的過程中,隻需要注意一個問題,必須讓注解生效,就需要開啟注解的支援
<!-- 指定要掃描的包,這個包下的注解就會生效-->
<context:component-scan base-package="com.kk"></context:component-scan>
<!-- 開啟注解的支援-->
<context:annotation-config/>
作用:
- 進行注解驅動注冊,進而使注解生效
- 用于激活那些已經在spring容器裡注冊過的bean上面的注解,也就是顯示的向Spring注冊
- 如果不掃描包,就需要手動配置bean
- 如果不加注解驅動,則注入的值為null!
7、基于Java的方式配置Spring
完全不使用Spring的xml配置,全權交給java來做!
JavaConfig 是Spring的一個子項目.在Spring之後,成為了一個核心功能!
測試
實體類:User
package com.kk.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
//使用這個注解的意思,就是說明這個類被Spring接管了,就是注冊到了容器中
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("kk")//屬性注入值
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置檔案:MyConfig
package com.kk.config;
import com.kk.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//@Configuration 代表這是一個配置類,就和beans.xml一樣的
//這個也會被Spring容器托管,注冊到容器中,因為它本來就是一個@Component
@Configuration
@ComponentScan("com.kk.pojo")
@Import(MyConfig2.class) //引入
public class MyConfig {
//注冊一個bean,就相當于之前寫的一個bean标簽
//這個方法的名字就相當于bean标簽中的id屬性
//這個方法的傳回值。就相當于bean标簽的的class屬性
@Bean
public User getUser(){
return new User();//就是傳回要注入到bean的對象
}
}
配置檔案:MyConfig2
package com.kk.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig2 {
}
測試類:
import com.kk.config.MyConfig;
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
//如果完全使用了配置類去做,我們就隻能通過AnnotationConfig 上下文來擷取容器,通過配置類的class對象加載!
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User getUser = (User)context.getBean("user");
System.out.println(getUser.getName());
}
}
8、靜态/動态代理模式
為什麼要學習代理模式,因為AOP的底層機制就是動态代理!
代理模式:
- 靜态代理
- 動态代理
8.1、靜态代理
靜态代理角色分析
- 抽象角色 : 一般使用接口或者抽象類來實作
- 真實角色 : 被代理的角色
- 代理角色 : 代理真實角色 ; 代理真實角色後 , 一般會做一些附屬的操作 .
- 客戶 : 使用代理角色來進行一些操作 .
代碼步驟:
1、接口
//租房的接口
public interface Rent {
public void rent();
}
2、真實角
//房東
public class Host {
public void rent(){
System.out.println("房東要出租房子");
}
}
3、代理角色
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
hetong();
fare();
}
//看房
public void seeHouse(){
System.out.println("帶房客看房");
}
//租賃
public void hetong(){
System.out.println("簽租賃合同");
}
//收中介費
public void fare(){
System.out.println("收中介費");
}
}
4、用戶端通路代理角色
public class Client {
public static void main(String[] args) {
//房東要租房子
Host host = new Host();
//代理,中介幫方法租房子
//代理操作一般會帶一些附屬操作
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
分析:在這個過程中,你直接接觸的就是中介,就如同現實生活中的樣子,你看不到房東,但是你依舊租到了房東的房子通過代理,這就是所謂的代理模式,程式源自于生活,是以學程式設計的人,一般能夠更加抽象的看待生活中發生的事情。
靜态代理的好處:
- 可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .
- 公共的業務由代理來完成 . 實作了業務的分工 ,
- 公共業務發生擴充時變得更加集中和友善 .
缺點 :
- 類多了 , 多了代理類 , 工作量變大了 . 開發效率降低 .
我們想要靜态代理的好處,又不想要靜态代理的缺點,是以 , 就有了動态代理 !
靜态代理再了解
1、建立一個抽象角色,比如咋們平時做的使用者業務,抽象起來就是增删改查!
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
2、我們需要一個真實對象來完成這些增删改查操作
//真實對象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一個使用者");
}
public void delete() {
System.out.println("删除了一個使用者");
}
public void update() {
System.out.println("更新了一個使用者");
}
public void query() {
System.out.println("查詢了一個使用者");
}
}
3、需求來了,現在我們需要增加一個日志功能,怎麼實作!
- 思路1 :在實作類上增加代碼 【麻煩!】
- 思路2:使用代理來做,能夠不改變原來的業務情況下,實作此功能就是最好的了!
4、設定一個代理類來處理日志!代理角色
//代理角色,在這裡面增加日志的實作
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("執行了"+msg+"方法");
}
}
5、測試通路類:
public class Client {
public static void main(String[] args) {
//真實業務
UserServiceImpl userService = new UserServiceImpl();
//代理類
UserServiceProxy proxy = new UserServiceProxy();
//使用代理類實作日志功能!
proxy.setUserService(userService);
proxy.add();
}
}
OK,到了現在代理模式大家應該都沒有什麼問題了,重點大家需要了解其中的思想;
我們在不改變原來的代碼的情況下,實作了對原有功能的增強,這是AOP中最核心的思想
AOP:縱向開發,橫向開發
8.2、動态代理
- 動态代理的角色和靜态代理的一樣 .
- 動态代理的代理類是動态生成的 . 靜态代理的代理類是我們提前寫好的
- 動态代理分為兩類 : 一類是基于接口動态代理 , 一類是基于類的動态代理
- 基于接口的動态代理----JDK動态代理
- 基于類的動态代理–cglib
- 現在用的比較多的是javasist來生成動态代理
- 我們這裡使用JDK的原生代碼來實作,其餘的道理都是一樣的!、
JDK的動态代理需要了解兩個類
核心 : InvocationHandler 和 Proxy
【InvocationHandler:調用處理程式】
Object invoke(Object proxy, 方法 method, Object[] args);
//參數
//proxy - 調用該方法的代理執行個體
//method -所述方法對應于調用代理執行個體上的接口方法的執行個體。方法對象的聲明類将是該方法聲明的接口,它可以是代理類繼承該方法的代理接口的超級接口。
//args -包含的方法調用傳遞代理執行個體的參數值的對象的陣列,或null如果接口方法沒有參數。原始類型的參數包含在适當的原始包裝器類的執行個體中,例如java.lang.Integer或java.lang.Boolean 。
【Proxy : 代理】
//生成代理類
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
代碼實作
Rent.java 即抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
Host.java 即真實角色
package com.kk.demo03;
//房東
public class Host implements Rent {
public void rent(){
System.out.println("房東要出租房子");
}
}
ProxyInvocationHandler. java 即代理角色
package com.kk.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//使用該類自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理類
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
}
//處理代理事例,并傳回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//動态代理的本質就是使用反射機制實作!
seeHouse();
fare();
Object result = method.invoke(rent, args);
return result;
}
public void seeHouse(){
System.out.println("中介帶看房子");
}
public void fare(){
System.out.println("收中介費");
}
}
Client.java
package com.kk.demo03;
public class Client {
public static void main(String[] args) {
//真實角色
Host host = new Host();
//代理角色:現在無
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通過調用程式處理角色來處理我們需要調用的接口對象!
pih.setRent(host);
Rent proxy =(Rent) pih.getProxy();//這裡的proxy就是動态代理
proxy.rent();
}
}
核心:一個動态代理 , 一般代理某一類業務 , 一個動态代理可以代理多個類,代理的是接口!
深化了解
我們來使用動态代理實作代理我們後面寫的UserService!
我們也可以編寫一個通用的動态代理實作的類!所有的代理對象設定為Object即可!
package com.kk.demo04;
import com.kk.demo03.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//使用該類自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理類
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
//處理代理事例,并傳回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//動态代理的本質就是使用反射機制實作!
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("執行了"+msg+"方法");
}
}
測試!
package com.kk.demo04;
import com.kk.demo02.UserService;
import com.kk.demo02.UserServiceImp;
public class Client {
public static void main(String[] args) {
//真實對象
UserServiceImp userService = new UserServiceImp();
//代理角色 不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);//pih調用userService,設定要代理的對象
//動态生成代理類
UserService proxy = (UserService)pih.getProxy();
proxy.add();
}
}
動态代理的好處
靜态代理有的它都有,靜态代理沒有的,它也有!
- 可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .
- 公共的業務由代理來完成 . 實作了業務的分工 ,
- 公共業務發生擴充時變得更加集中和友善 .
- 一個動态代理 , 一般代理某一類業務
- 一個動态代理可以代理多個類,代理的是接口!
9、AOP
9.1、什麼是AOP
AOP(Aspect Oriented Programming)意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
9.2、Aop在Spring中的作用
提供聲明式事務;允許使用者自定義切面
以下名詞需要了解下:
- 橫切關注點:跨越應用程式多個子產品的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日志 , 安全 , 緩存 , 事務等等 …
- 切面(ASPECT):橫切關注點 被子產品化 的特殊對象。即,它是一個類。
- 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。
- 目标(Target):被通知對象。
- 代理(Proxy):向目标對象應用通知之後建立的對象。
- 切入點(PointCut):切面通知 執行的 “地點”的定義。
- 連接配接點(JointPoint):與切入點比對的執行點。
SpringAOP中,通過Advice定義橫切邏輯,Spring中支援5種類型的Advice:
9.3、使用Spring實作Aop
【重點】使用AOP織入,需要導入一個依賴包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
第一種方式
9.3.1、通過 Spring API 實作【主要Spring API接口實作】
首先編寫我們的業務接口和實作類
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
public class UserServiceImp implements UserService{
@Override
public void add() {
System.out.println("增加了一個使用者");
}
@Override
public void delete() {
System.out.println("删除了一個使用者");
}
@Override
public void update() {
System.out.println("修改了一個使用者");
}
@Override
public void select() {
System.out.println("查詢了一個使用者");
}
}
然後去寫我們的增強類 , 我們編寫兩個 , 一個前置增強 一個後置增強
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice {
//method 要執行的目标對象的方法
//args:參數
//target目标對象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
}
}
package com.kk.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//returnValue 傳回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("執行了"+method.getName()+"方法,傳回結果為:"+returnValue);
}
}
最後去spring的檔案中注冊 , 并實作aop切入實作 , 注意導入限制 .
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注冊bean-->
<bean id="userService" class="com.kk.service.UserServiceImp"></bean>
<bean id="log" class="com.kk.log.log"></bean>
<bean id="afterLog" class="com.kk.log.AfterLog"></bean>
<!--方式一:使用原生的Spring API接口-->
<!-- 配置aop,需要aop的限制-->
<aop:config >
<!-- 需要切入點 expression:表達式 execution() 要執行的位置-->
<aop:pointcut id="poincut" expression="execution(* com.kk.service.UserServiceImp.*(..))"></aop:pointcut>
<!-- 執行環繞增加! -->
<aop:advisor advice-ref="log" pointcut-ref="poincut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="poincut"/>
</aop:config>
</beans>
測試
import com.kk.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//動态代理 代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
第二種方式
9.3.2、自定義類來實作Aop【主要是切面定義】
目标業務類不變依舊是userServiceImpl
第一步 : 寫我們自己的一個切入類
public class DiyPointcut {
public void before(){
System.out.println("---------方法執行前---------");
}
public void after(){
System.out.println("---------方法執行後---------");
}
}
去spring中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注冊bean-->
<bean id="userService" class="com.kk.service.UserServiceImp"></bean>
<bean id="log" class="com.kk.log.log"></bean>
<bean id="afterLog" class="com.kk.log.AfterLog"></bean>
<!-- 方式二,自定義類-->
<bean id="diy" class="com.kk.diy.DiyPointCut"></bean>
<aop:config>
<!-- 自定義切面,使用ref調用要引用的類-->
<aop:aspect ref="diy">
<!-- 切入點-->
<aop:pointcut id="point" expression="execution(* com.kk.service.UserServiceImp.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
測試:
public class MyTest {
@Test
public void test(){
ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
第三種方式
9.3.3、使用注解實作
第一步:編寫一個注解實作的增強類
package com.kk.diy;
//方式三:使用注解方式實作AOP
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //使用注解标記這個類是一個切面
public class AnnotationPointCut {
@Before("execution(* com.kk.service.UserServiceImp.*(..))") //注解的内容 應寫入插入點
public void before(){
System.out.println("===========方法執行前==========");
}
@After("execution(* com.kk.service.UserServiceImp.*(..))")
public void after(){
System.out.println("===========方法執行後==========");
}
//在環繞增加中,我們可以給定一個參數,代表我們要擷取處理的切入點
@Around("execution(* com.kk.service.UserServiceImp.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環繞前");
Object proceed = jp.proceed(); //執行方法
System.out.println("環繞後");
Signature signature = jp.getSignature();//獲得簽名
System.out.println("signature:"+signature);
System.out.println(proceed);
}
}
第二步:在Spring配置檔案中,注冊bean,并增加支援注解的配置
<!-- 方式三:使用注解實作AOP-->
<bean id="AnnotationPointCut" class="com.kk.diy.AnnotationPointCut"></bean>
<!-- 開啟注解支援 jdk(預設) proxy-target-class="false" cglib proxy-target-class="true" -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
aop:aspectj-autoproxy:說明
通過aop命名空間的<aop:aspectj-autoproxy />聲明自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面。當然,spring 在内部依舊采用AnnotationAwareAspectJAutoProxyCreator進行自動代理的建立工作,但具體實作的細節已經被<aop:aspectj-autoproxy />隐藏起來了
<aop:aspectj-autoproxy />有一個proxy-target-class屬性,預設為false,表示使用jdk動态代理織入增強,當配為<aop:aspectj-autoproxy poxy-target-class="true"/>時,表示使用CGLib動态代理技術織入增強。不過即使proxy-target-class設定為false,如果目标類沒有聲明接口,則spring将自動使用CGLib動态代理。
10、整合MyBatis
步驟
1、導入相關jar包
junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
mybatis
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
mysql-connector-java
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
spring相關
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
aspectJ AOP 織入器
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
mybatis-spring整合包 【重點】
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
配置Maven靜态資源過濾問題!
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
2、編寫配置檔案
3、代碼實作
10.1、回憶MyBatis
1、編寫pojo實體類
package com.kk.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密碼
}
2、實作mybatis的配置檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kk.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&
useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&
allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.kk.mapper.UserMapper"/>
</mappers>
</configuration>
3、UserDao接口編寫
package com.kk.mapper;
import com.kk.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
}
4、接口對應的Mapper映射檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kk.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from mybatis.user
</select>
</mapper>
5、測試類
import com.kk.mapper.UserMapper;
import com.kk.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void selectUser() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user: userList){
System.out.println(user);
}
sqlSession.close();
}
}
10.2、MyBatis-Spring學習
引入Spring之前需要了解mybatis-spring包中的一些重要類;
http://www.mybatis.org/spring/zh/index.html
什麼是 MyBatis-Spring?
MyBatis-Spring 會幫助你将 MyBatis 代碼無縫地整合到 Spring 中。
知識基礎
在開始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 這兩個架構和有關它們的術語。這很重要
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 架構 | Spring Batch | Java |
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
如果使用 Maven 作為建構工具,僅需要在 pom.xml 中加入以下代碼即可:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
要和 Spring 一起使用 MyBatis,需要在 Spring 應用上下文中定義至少兩樣東西:一個 SqlSessionFactory 和至少一個資料映射器類。
在 MyBatis-Spring 中,可使用SqlSessionFactoryBean來建立 SqlSessionFactory。要配置這個工廠 bean,隻需要把下面代碼放在 Spring 的 XML 配置檔案中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
注意:SqlSessionFactory需要一個 DataSource(資料源)。這可以是任意的 DataSource,隻需要和配置其它 Spring 資料庫連接配接一樣配置它就可以了。
在基礎的 MyBatis 用法中,是通過 SqlSessionFactoryBuilder 來建立 SqlSessionFactory 的。而在 MyBatis-Spring 中,則使用 SqlSessionFactoryBean 來建立。
在 MyBatis 中,你可以使用 SqlSessionFactory 來建立 SqlSession。一旦你獲得一個 session 之後,你可以使用它來執行映射了的語句,送出或復原連接配接,最後,當不再需要它的時候,你可以關閉 session。
SqlSessionFactory有一個唯一的必要屬性:用于 JDBC 的 DataSource。這可以是任意的 DataSource 對象,它的配置方法和其它 Spring 資料庫連接配接是一樣的。
一個常用的屬性是 configLocation,它用來指定 MyBatis 的 XML 配置檔案路徑。它在需要修改 MyBatis 的基礎配置非常有用。通常,基礎配置指的是 < settings> 或 < typeAliases>元素。
需要注意的是,這個配置檔案并不需要是一個完整的 MyBatis 配置。确切地說,任何環境配置(),資料源()和 MyBatis 的事務管理器()都會被忽略。SqlSessionFactoryBean 會建立它自有的 MyBatis 環境配置(Environment),并按要求設定自定義環境的值。
SqlSessionTemplate 是 MyBatis-Spring 的核心。作為 SqlSession 的一個實作,這意味着可以使用它無縫代替你代碼中已經在使用的 SqlSession。
模闆可以參與到 Spring 的事務管理中,并且由于其是線程安全的,可以供多個映射器類使用,你應該總是用 SqlSessionTemplate 來替換 MyBatis 預設的 DefaultSqlSession 實作。在同一應用程式中的不同類之間混雜使用可能會引起資料一緻性的問題。
可以使用 SqlSessionFactory 作為構造方法的參數來建立 SqlSessionTemplate 對象。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
現在,這個 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一個 SqlSession 屬性,就像下面這樣:
package com.kk.mapper;
import com.kk.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImp implements UserMapper{
//在原來,所有的操作,都使用SqlSession來執行; 現在都使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession){
this.sqlSession=sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
按下面這樣,注入 SqlSessionTemplate:
<bean id="userMapper" class="com.kk.mapper.UserMapperImp">
<property name="sqlSession" ref="sqlSession"/>
</bean>
整合實作一
1、引入Spring配置檔案beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
2、配置資料源替換mybaits的資料源
<!-- DataSource使用Spring的資料源替換Mybatis的配置 c3p0 dbcp druid-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&
useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&
allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
3、配置SqlSessionFactory,關聯MyBatis
<!-- sqlSeesionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 綁定mybatis配置檔案-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kk/mapper/*.xml"/>
</bean>
4、注冊sqlSessionTemplate,關聯sqlSessionFactory;
<!-- SqlSessionTemplate就是我們使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 隻能用構造器注入sqlSessionFactory,因為它沒有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
5、增加Mapper接口的實作類;私有化sqlSessionTemplate
package com.kk.mapper;
import com.kk.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImp implements UserMapper{
//在原來,所有的操作,都使用SqlSession來執行; 現在都使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession){
this.sqlSession=sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
6、注冊bean實作
<bean id="userMapper" class="com.kk.mapper.UserMapperImp">
<property name="sqlSession" ref="sqlSession"/>
</bean>
7、測試
public class MyTest {
@Test
public void selectUser() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
結果成功輸出!現在我們的Mybatis配置檔案的狀态!發現都可以被Spring整合!
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kk.pojo"/>
</typeAliases>
</configuration>
整合實作二
mybatis-spring1.2.3版以上的才有這個 .
官方文檔截圖 :
dao繼承Support類 , 直接利用 getSqlSession() 獲得 , 然後直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且對事務的支援更加友好 . 可跟蹤源碼檢視
測試:
1、将我們上面寫的UserMapperImp修改一下
package com.kk.mapper;
import com.kk.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImp2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
// SqlSession sqlSession = getSqlSession();
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// return mapper.selectUser();
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
2、修改bean的配置
<bean id="userMapper2" class="com.kk.mapper.UserMapperImp2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
3、測試
public class MyTest {
@Test
public void selectUser() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
11、聲明式事務
- 事務在項目開發過程非常重要,涉及到資料的一緻性的問題,不容馬虎!
- 事務管理是企業級應用程式開發中必備技術,用來確定資料的完整性和一緻性。
- 事務就是把一系列的動作當成一個獨立的工作單元,這些動作要麼全部完成,要麼全部不起作用。
事務四個屬性ACID
原子性(atomicity)
- 事務是原子性操作,由一系列動作組成,事務的原子性確定動作要麼全部完成,要麼完全不起作用
一緻性(consistency)
- 一旦所有事務動作完成,事務就要被送出。資料和資源處于一種滿足業務規則的一緻性狀态中
隔離性(isolation)
- 可能多個事務會同時處理相同的資料,是以每個事務都應該與其他事務隔離開來,防止資料損壞
持久性(durability)
- 事務一旦完成,無論系統發生什麼錯誤,結果都不會受到影響。通常情況下,事務的結果被寫到持久化存儲器中
測試
将上面的代碼拷貝到一個新項目中
在之前的案例中,我們給userDao接口新增兩個方法,删除和增加使用者;
//添加一個使用者
int addUser(User user);
//根據id删除使用者
int deleteUser(int id);
mapper檔案,我們故意把 deletes 寫錯,測試!
<insert id="addUser" parameterType="com.kk.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>
編寫接口的實作類,在實作類中,我們去操作一波
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
//增加一些操作
public List<User> selectUser() {
User user = new User(8,"bb","123456");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(4);
return mapper.selectUser();
}
//新增
public int addUser(User user) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.addUser(user);
}
//删除
public int deleteUser(int id) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.deleteUser(id);
}
}
測試
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.selectUser();
}
}
Resources:
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kk.pojo"/>
</typeAliases>
</configuration>
spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DataSource使用Spring的資料源替換Mybatis的配置 c3p0 dbcp druid-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&
useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&
allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- sqlSeesionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 綁定mybatis配置檔案-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kk/mapper/*.xml"/>
</bean>
<!-- SqlSessionTemplate就是我們使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 隻能用構造器注入sqlSessionFactory,因為它沒有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.kk.mapper.UserMapperImp">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.kk.mapper.UserMapperImp">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
Spring在不同的事務管理API之上定義了一個抽象層,使得開發人員不必了解底層的事務管理API就可以使用Spring的事務管理機制。Spring支援程式設計式事務管理和聲明式的事務管理。
程式設計式事務管理
- 将事務管理代碼嵌到業務方法中來控制事務的送出和復原
- 缺點:必須在每個事務操作業務邏輯中包含額外的事務管理代碼
聲明式事務管理
- 一般情況下比程式設計式事務好用。
- 将事務管理代碼從業務方法中分離出來,以聲明的方式來實作事務管理。
- 将事務管理作為橫切關注點,通過aop方法子產品化。Spring中通過Spring AOP架構支援聲明式事務管理。
JDBC事務
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
配置好事務管理器後我們需要去配置事務的通知
<!--配置事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什麼樣的事務,配置事務的傳播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
spring事務傳播特性:
事務傳播行為就是多個事務方法互相調用時,事務如何在這些方法間傳播。spring支援7種事務傳播行為:
- propagation_requierd:如果目前沒有事務,就建立一個事務,如果已存在一個事務中,加入到這個事務中,這是最常見的選擇。
- propagation_supports:支援目前事務,如果沒有目前事務,就以非事務方法執行。
- propagation_mandatory:使用目前事務,如果沒有目前事務,就抛出異常。
- propagation_required_new:建立事務,如果目前存在事務,把目前事務挂起。
- propagation_not_supported:以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。
- propagation_never:以非事務方式執行操作,如果目前事務存在則抛出異常。
- propagation_nested:如果目前存在事務,則在嵌套事務内執行。如果目前沒有事務,則執行與propagation_required類似的操作
Spring 預設的事務傳播行為是 PROPAGATION_REQUIRED,它适合于絕大多數的情況。
假設 ServiveX#methodX() 都工作在事務環境下(即都被 Spring 事務增強了),假設程式中存在如下的調用鍊:Service1#method1()->Service2#method2()->Service3#method3(),那麼這 3 個服務類的 3 個方法通過 Spring 的事務傳播機制都工作在同一個事務中。
就好比,我們剛才的幾個方法存在調用,是以會被放在一組事務當中!
配置AOP
導入aop的頭檔案!
<!--配置aop織入事務-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
删掉剛才插入的資料,再次測試!
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper mapper = (UserMapper) context.getBean("UserMapper");
List<User> user = mapper.selectUser();
System.out.println(user);
}
r" class=“com.kk.mapper.UserMapperImp”>
```
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.kk.mapper.UserMapperImp">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
Spring在不同的事務管理API之上定義了一個抽象層,使得開發人員不必了解底層的事務管理API就可以使用Spring的事務管理機制。Spring支援程式設計式事務管理和聲明式的事務管理。
程式設計式事務管理
- 将事務管理代碼嵌到業務方法中來控制事務的送出和復原
- 缺點:必須在每個事務操作業務邏輯中包含額外的事務管理代碼
聲明式事務管理
- 一般情況下比程式設計式事務好用。
- 将事務管理代碼從業務方法中分離出來,以聲明的方式來實作事務管理。
- 将事務管理作為橫切關注點,通過aop方法子產品化。Spring中通過Spring AOP架構支援聲明式事務管理。
JDBC事務
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
配置好事務管理器後我們需要去配置事務的通知
<!--配置事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什麼樣的事務,配置事務的傳播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
spring事務傳播特性:
事務傳播行為就是多個事務方法互相調用時,事務如何在這些方法間傳播。spring支援7種事務傳播行為:
- propagation_requierd:如果目前沒有事務,就建立一個事務,如果已存在一個事務中,加入到這個事務中,這是最常見的選擇。
- propagation_supports:支援目前事務,如果沒有目前事務,就以非事務方法執行。
- propagation_mandatory:使用目前事務,如果沒有目前事務,就抛出異常。
- propagation_required_new:建立事務,如果目前存在事務,把目前事務挂起。
- propagation_not_supported:以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。
- propagation_never:以非事務方式執行操作,如果目前事務存在則抛出異常。
- propagation_nested:如果目前存在事務,則在嵌套事務内執行。如果目前沒有事務,則執行與propagation_required類似的操作
Spring 預設的事務傳播行為是 PROPAGATION_REQUIRED,它适合于絕大多數的情況。
假設 ServiveX#methodX() 都工作在事務環境下(即都被 Spring 事務增強了),假設程式中存在如下的調用鍊:Service1#method1()->Service2#method2()->Service3#method3(),那麼這 3 個服務類的 3 個方法通過 Spring 的事務傳播機制都工作在同一個事務中。
就好比,我們剛才的幾個方法存在調用,是以會被放在一組事務當中!
配置AOP
<!--配置aop織入事務-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper mapper = (UserMapper) context.getBean("UserMapper");
List<User> user = mapper.selectUser();
System.out.println(user);
}