文章目錄
Spring的基本概念和基本使用
說明:學習筆記是對B站狂神說的Spring視頻的整理和總結。
Spring概述
Spring相關的網址:
Spring官網
Spring各版本官方下載地址
Spring的GitHub
優點:
- Spring是一個開源免費的框架(容器)
- Spring是一個輕量級的、非侵入式的框架(非侵入指的是不會對現有代碼產生影響)
- 控制反轉(IOC) , 面向切面編程(AOP)
- 支持事務的處理 , 對框架整合的支持
總結:Spring是一個輕量級的控制反轉(IoC)和麪向切面(AOP)的容器(框架)。
Spring項目的創建
創建一個Spring項目,最主要的過程是以下幾個步驟。
1、添加依賴
由maven項目來創建Spring項目。首先在maven的配置文件pom.xml中添加主要的依賴:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--webmvc依賴添加進來,其它很多依賴也就添加進來了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<!--jdbc的依賴,後期整合Mybatis的時候會用到-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2、配置文件
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
java創建對象:類型 變量名 = new 類型();
Spring通過bean來創建對象:
id:變量名
class:new的對象
property:相當於給對象中的屬性設置一個值
-->
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
可以理解爲一個類就是一個bean,在Spring中沒有顯式的new關鍵字,而是通過bean在Spring容器中來創建對象。
3、獲取對象
Spring讀取含有bean的配置文件,在容器中實例化各個類,得到ApplicationContext類型的對象,通過此對象來獲得各個實例。
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
userService.getUser();
}
}
Java中DAO層、Service層和Controller層的區別:
- DAO層:數據訪問層,屬於底層的操作,具體對應某個表的增刪改查,也就是說某個DAO一定是和數據庫的某一張表一一對應的。
- Service層:服務層,是對一個或多個DAO進行的再次封裝,封裝成一個服務,所以這裏也就不會是一個原子操作了,需要事務控制。
- Controler層:Controler層負責請求轉發,接受頁面過來的參數,傳給Service處理,接到返回值,再傳給頁面。
Spring的配置
Spring的配置文件中主要有以下幾種配置,最主要最重要的是bean的配置。
1、別名
<!--user是某個bean的唯一標識符,user2是這個bean的別名-->
<alias name="user" alias="user2"/>
2、Bean的配置
<!--
id:bean的唯一標識符
class:bean對象所對應的全限定名,即包名+類型
name:也是別名,而且name可以取多個別名
-->
<bean id="user" class="com.xlq.pojo.User" name="user2">
<constructor-arg name="name" value="xlq"/>
<constructor-arg name="age" value="25"/>
</bean>
3、import
- 用於團隊開發使用,將多個人寫的beans.xml合併爲一個總的applicationContext.xml。
- 使用的時候直接加載總的配置文件即可。
IoC的基本概念
由問題引出IoC
傳統的Dao層和Service層的文件構成:
- UserDao–dao層的底層接口
- UserDaoImpl–實現類
- UserService–業務接口
- UserServiceImpl–業務實現類
在業務層中,用戶的需求可能會影響我們原來的代碼,我們需要根據用戶的需求去修改源代碼,如果程序量十分龐大,修改一次的成本代價十分昂貴。
第一版代碼:
public class UserServiceImpl implements UserService {
// private UserDao userDao = new UserDaoImpl();
// 如果用戶想從Mysql中獲取數據,有一個問題:這樣的代碼適應不了用戶需求的變更
private UserDao userDao = new UserDaoMysqlImpl();
// 在業務層調DAO層
public void getUser() {
userDao.getUser();
}
}
如果使用set方法進入動態注入,將會發生革命性的變化。
第二版代碼:
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set方法進入動態注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 在業務層調DAO層
public void getUser() {
userDao.getUser();
}
}
控制反轉的意思:
- 第一版代碼中,userDao是程序員主動創建的,控制權在程序員手上。
- 第二版代碼中,通過set方法動態注入,程序不再具有主動性,而是變成了被動地接收對象。
IoC的本質
IoC(Inversion of Control,控制反轉),是一種設計思想,DI(Dependency Injection,依賴注入)是實現IoC的一種方法,也有人認爲DI只是IoC的另一種說法。沒有IoC的程序中 , 我們使用面向對象編程 , 對象的創建與對象間的依賴關係完全硬編碼在程序中,對象的創建由程序自己控制,控制反轉後將對象的創建轉移給第三方,個人認爲所謂控制反轉就是:獲得依賴對象的方式反轉了。
控制反轉是一種通過描述(XML或註解)並通過第三方去生產或獲取特定對象的方式。在Spring中實現控制反轉的是IoC容器,其實現方法是依賴注入。
IoC創建對象的方式
第一類:無參構造。Spring默認使用無參構造創建對象。
第二類:我們需要使用有參構造創建對象。官網提供了三種方式。
- 下標賦值
<bean id="user" class="com.xlq.pojo.User">
<constructor-arg index="0" value="xlq"/>
</bean>
- 類型
<!--如果有多個參數,不推薦使用-->
<bean id="user" class="com.xlq.pojo.User">
<constructor-arg type="java.lang.String" value="xlq"/>
</bean>
- 參數名
<bean id="user" class="com.xlq.pojo.User">
<constructor-arg name="name" value="xlq"/>
<constructor-arg name="age" value="25"/>
</bean>
總結:在配置文件加載的時候,容器中管理的對象就已經初始化了!
依賴注入(DI)
1、構造器注入
2、Set方法注入【重點】
依賴注入,即Set注入,含義是什麼?
- 依賴:bean對象的創建依賴於容器。
- 注入:bean對象中的所有屬性,由容器來注入!
示例:
類信息爲:
public class Address {
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" + "addr='" + addr + '\'' + '}';
}
}
public class Student {
private String name;//基本類型
private Address addr;//引用類型
private String[] books;
private Set<String> hobbies;
private Map<String, String> cards;
private List<String> games;
private String wife;// 測試null的注入
private Properties info;
// 這裏還必須要有各個屬性的Set方法,太長先省略
}
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">
<bean id="address" class="com.xlq.pojo.Address">
<property name="addr" value="nanjing"/>
</bean>
<bean id="student" class="com.xlq.pojo.Student">
<!--基本類型的注入-->
<property name="name" value="xiaoming"/>
<!--引用類型的注入-->
<property name="addr" ref="address"/>
<!--數組類型的注入-->
<property name="books">
<array>
<value>紅樓夢</value>
<value>西遊記</value>
<value>水滸傳</value>
<value>三國演義</value>
</array>
</property>
<!--下面三個是集合類的注入-->
<property name="hobbies">
<set>
<value>唱歌</value>
<value>跑步</value>
<value>打羽毛球</value>
</set>
</property>
<property name="cards">
<map>
<entry key="身份證" value="123456"/>
<entry key="銀行卡" value="123456"/>
</map>
</property>
<property name="games">
<list>
<value>LOL</value>
<value>CS</value>
<value>GTA5</value>
</list>
</property>
<!--空值null的注入-->
<property name="wife">
<null/>
</property>
<!--Properties的注入-->
<property name="info">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
3、擴展方法注入–p命名空間和c命名空間
實體類:
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", age=" + age +'}';
}
}
配置文件beans2.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空間注入,可以直接注入屬性的值,p表示property-->
<bean id="user" class="com.xlq.pojo.User" p:age="18" p:name="xxx"/>
<!--c命名空間的注入,通過構造器注入:construct-args-->
<bean id="user2" class="com.xlq.pojo.User" c:age="18" c:name="xxx"/>
</beans>
注意點:p命名和c命名空間不能直接使用,需要導入xml約束。
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
Bean的作用域
官網中關於Bean的作用域的說明:
簡要介紹:
- 單例模式【Spring默認機制】
- 原型模式:每次從容器中get的時候,都會產生一個新的對象。
- 其餘的request、session、application這些只能在web開發中使用到。
Bean的自動裝配
自動裝配是Spring滿足bean依賴的一種方式。Spring會在上下文中自動尋找,並自動給bean裝配屬性。
Spring中有三種裝配的方式:
- 在xml中顯式配置;
- 在Java中顯式配置;
- 隱式地自動裝配bean【重要】
1、ByName自動裝配
<bean id="dog" class="com.xlq.pojo.Dog"/>
<bean id="cat" class="com.xlq.pojo.Cat"/>
<!--Persong含有兩個Set方法:
public void setDog(Dog dog)
public void setCat(Cat cat)-->
<bean id="person" class="com.xlq.pojo.Person" autowire="byName">
<property name="name" value="xxx"/>
</bean>
2、ByType自動裝配
<bean class="com.xlq.pojo.Dog"/>
<bean class="com.xlq.pojo.Cat"/>
<bean id="person" class="com.xlq.pojo.Person" autowire="byType">
<property name="name" value="xxx"/>
</bean>
區別:
- byname的時候,需要保證自動裝配的bean的id唯一,並且id的名字需要和自動注入的set方法的形參名字一一對應;
- bytype的時候,需要保證自動裝配的bean的class唯一,並且class的名字需要和自動注入的set方法的形參類型一一對應;
3、註解實現自動裝配
使用註解的步驟:
- 導入約束:context約束
- 配置註解的支持:context:annotation-config
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"><!--導入約束-->
<!--配置註解的支持-->
<context:annotation-config/>
</beans>
@Autowired
基本使用:直接在屬性上使用即可,也可以在set方法上使用。
使用Autowired我們可以不用編寫set方法了,前提是這個自動裝配的屬性在IoC容器中存在,且符合名字byname。
public class Person {
private String name;
// 如果顯式定義了Autowired屬性爲false,說明這個對象可以爲null,否則不允許爲空
@Autowired(required = false)
private Dog dog;
@Autowired
private Cat cat;
}
如果@Autowired自動裝配的環境比較複雜,自動裝配無法通過一個註解【@Autowired】完成的時候,我們可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一個唯一的bean對象注入。
public class Person {
private String name;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
@Autowired
private Cat cat;
}
@Resource
public class Person {
private String name;
@Resource(name = "dog2")
private Dog dog;
@Resource
private Cat cat;
@Resource和@Autowired的區別:
- 都是用來自動裝配的,都可以放在屬性字段上;
- @Autowired通過bytype的方式實現,而且必須要求這個對象存在。
- @Resource默認通過byname的方式實現,如果找不到名字,則通過bytype實現,如果兩個都找不到則報錯。
使用註解開發
前置條件
- 在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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
常用註解
@Autowired
:自動裝配通過類型,名字;如果@Autowired
不能唯一自動裝配,則需要通過@Qualifier(value="xxx")
@Nullable
:字段標記了這個註解,說明這個字段可以爲null@Resource
:自動裝配通過名字,類型@Component
:組件,放在類上,說明這個類被Spring管理了,就是bean。@Component
有幾個衍生註解,下面幾個衍生註解的含義與@Component
相同。在web開發中,會按照MVC三層架構分層:- dao層:
@Repository
- service層:
@Service
- controller層:
@Controller
- dao層:
@Scope
:作用域
@Component
@Scope("singleton")
public class User {
@Value("xlq")
public String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
XML和註解使用總結:
- XML更加萬能,適用於任何場合,維護簡單方便。
- 註解不是自己的類不能使用,也就是說,註解只是針對使用註解的這個類。維護相對複雜。
- 最佳實踐:XML用來管理bean,註解只負責注入屬性。
JavaConfig
注:在Springboot中較爲常見。
實體類:
@Component
public class User {
@Value("xxx")
public String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置類:通過Java類來進行配置,代替了原先配置文件的作用。
// @Configuration表示這是一個配置類,作用相當於是beans.xml
// 同時也會註冊到Spring容器中,本質上是一個@Component
@Configuration
@ComponentScan("com.xlq") // 掃描指定包下的註解
public class MyConfig {
// 註冊一個bean,作用相當於是原先配置文件中的<bean>標籤
// 這個方法的名字,相當於<bean>標籤中的id屬性
// 這個方法的返回值,相當於<bean>標籤中的class屬性
@Bean
public User getUser() {
return new User();
}
}
測試類:
public class MyTest {
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user);
}
}
代理模式
代理模式:Spring AOP的底層!
代理模式的分類:
- 靜態代理
- 動態代理
靜態代理
代理模式示例:
角色介紹:
- 抽象角色:一般會使用接口或者抽象類來實現。
- 真實角色:被代理的角色。比如:房東。
- 代理角色:代理真實角色,代理真實角色後,還會有一些附屬操作,比如:房屋中介,附屬操作是看房子、籤合同等。
- 客戶端:訪問代理角色的人。比如:租戶。
代碼步驟:
- 接口
- 真實角色
- 代理角色
- 客戶端訪問代理角色
代理模式的好處:
- 可以使真實角色的操作更加純粹,不用去關注一些公共的業務(比如看房、籤合同)
- 公共業務都交給代理模式,實現了業務的分工。
- 公共業務發生擴展的時候,方便集中管理。
缺點:一個真實角色就會產生一個代理角色,代碼量會翻倍,開發效率會降低。(從代碼的角度來理解,創建中介對象的時候會傳入一個房東對象,體現出了靜態代理)
動態代理
動態代理中的各個角色和靜態代理是一致的。但是動態代理的代理類是動態生成的,不是我們直接寫好的。
動態代理分爲兩大類:基於接口的動態代理和基於類的動態代理。
- 基於接口:JDK動態代理
- 基於類:cglib
- Java字節碼實現:JAVAsist
動態代理的好處:
- 一個動態代理類代理的是一個接口,接口對應的是某一類業務,比如:房屋租賃業務。
- 一個動態代理類可以代理多個類,這些類實現同一個接口即可。
動態代理中兩個重要的類和接口:Proxy(類)和InvocationHandler(接口)
- InvocationHandler接口裏面只有一個invoke()方法,該方法是proxy代理實例的調用處理程序。每一個proxy代理實例都有一個關聯的調用處理程序;代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke方法。invoke方法如下:
/**
* proxy:代理對象
* method:代理對象調用接口方法所對應的Method實例。注:我們是通過代理對象來調用真實對象中的方法。
* args:指代理對象調用方法傳遞的參數
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
- Proxy類就是用來創建一個代理對象的類,它提供了很多方法,但是我們最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
三個參數的含義:
- loader:一個Classloader對象,定義了由哪個Classloader對象對生成的代理類進行加載;
- interfaces:一個interface對象數組,表示我們將要給我們的代理對象提供一組什麼樣的接口,如果我們提供了這樣一個接口對象數組,那麼也就是聲明瞭代理類實現了這些接口,代理類就可以調用接口中聲明的所有方法。
- h:一個InvocationHandler對象,表示的是當動態代理對象調用方法的時候會關聯到哪一個InvocationHandler對象上,並最終由其調用。
參考資料:
Java動態代理InvocationHandler和Proxy學習筆記
Java中InvocationHandler接口中第一個參數proxy詳解
AOP
AOP的實現機制:
AOP的實現方式
前提是先要導入AOP的依賴:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用原生Spring API接口
新增日誌業務:
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
}
}
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("執行了"+method.getName()+"方法,返回結果爲:"+returnValue);
}
}
配置文件中註冊bean和配置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.xlq.service.UserServiceImpl"/>
<bean id="log" class="com.xlq.log.Log"/>
<bean id="afterLog" class="com.xlq.log.AfterLog"/>
<!--使用AOP方式一:使用原生Spring API接口-->
<!--配置AOP:需要導入AOP的約束-->
<aop:config>
<!--切入點
id:該切入點的唯一標識符
expression:按照表達式進行匹配
第一個 * 表示任意返回值
com.xlq.service.UserServiceImpl.*表示匹配UserServiceImpl類裏面的任意方法
(..)表示有無參數均可,有參數的話可以是任意參數-->
<aop:pointcut id="pointcut" expression="execution(* com.xlq.service.UserServiceImpl.*(..))"/>
<!--執行環繞增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
測試類:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.delete();
}
}
方式二:自定義切面來實現AOP
要點:
- 在配置文件中自定義切面,切面實際上就是一個類,所以切面要和一個類進行關聯。
- 在切面裏面定義切入點
<aop:pointcut>
和相應的通知機制<aop:before>
、<aop:after>
。
自定義類DiyPoint,所在的包是com.xlq.diy
public class DiyPointCut {
public void before() {
System.out.println("在切入點之前");
}
public void after() {
System.out.println("在切入點之後");
}
}
配置文件:
<!--自定義類-->
<bean id="diy" class="com.xlq.diy.DiyPointCut"/>
<aop:config>
<!--自定義切面,ref爲要引入的類-->
<aop:aspect ref="diy">
<!--切入點-->
<aop:pointcut id="point" expression="execution(* com.xlq.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:使用註解來實現。
自定義類AnnotationPointCut.java
@Aspect // 標註這個類是一個切面
public class AnnotationPointCut {
@Before("execution(* com.xlq.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("方法執行之前");
}
@After("execution(* com.xlq.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("方法執行之後");
}
// 在環繞增強中,我們可以給定一個參數,代表我們要獲取處理切入的點
@Around("execution(* com.xlq.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環繞前");
Signature signature = jp.getSignature();
System.out.println("signature: "+signature);
Object proceed = jp.proceed();// 執行方法
System.out.println("環繞後");
}
}
XML配置:
<bean id="annotationPointCut" class="com.xlq.diy.AnnotationPointCut"/>
<!--開啓註解支持-->
<aop:aspectj-autoproxy/>
整合MyBatis
參考資料:mybatis-spring官方文檔
一般步驟(推薦)
1、數據源
2、sqlSessionFactory
3、sqlSessionTemplate
4、接口的實現類
<!--用Spring來管理數據源-->
<!--DataSource:使用Spring的數據源來替換Mybatis的配置 c3p0 dpcp druid
使用Spring提供的JDBC:org.springframework.jdbc.datasource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<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/xlq/dao/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用構造器注入sqlSessionFactory,因爲SqlSessionTemplate沒有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
UserMapper接口的實現類:
public class UserMapperImpl implements UserMapper {
// 我們的所有操作,原來使用sqlSession來執行,現在是使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserList();
}
}
還需要把實現類注入到Spring中:
<bean id="userMapper" class="com.xlq.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
SqlSessionDaoSupport
獲取SqlSession的另外一種方法。SqlSessionDaoSupport 是一個抽象的支持類,用來爲你提供 SqlSession。調用 getSqlSession() 方法你會得到一個 SqlSessionTemplate,之後可以用於執行 SQL 方法。
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUserList() {
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
}
SqlSessionDaoSupport 需要通過屬性設置一個 sqlSessionFactory 或 SqlSessionTemplate。如果兩個屬性都被設置了,那麼 SqlSessionFactory 將被忽略。 配置信息如下:
<bean id="userMapper2" class="com.xlq.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
事務
事務概述
- 把一組業務當成一個業務來做,要麼都成功,要麼都失敗;
- 事務在項目開發中,十分重要,設計到數據的一致性問題,不能馬虎;
- 確保完整性和一致性。
事務ACID特性:
- 原子性
- 一致性
- 隔離性:多個業務可能操作同一個資源,防止數據損壞。
- 持久性:事務一旦提交,無論系統發生什麼問題,結果都不會再被影響,被持久得寫到存儲器中。
事務管理的引入
在之前的Spring整合Mybatis的工程中,用戶Mapper實現類中加入插入用戶和刪除用戶的功能。
public class UserMapperImpl implements UserMapper {
// 我們的所有操作,原來使用sqlSession來執行,現在是使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(6, "lili", "123456");
mapper.insertUser(user);
mapper.deleteUser(6);
return mapper.getUserList();
}
public void insertUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insertUser(user);
}
public void deleteUser(int id) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(6);
}
}
配置文件:
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace就是綁定一個對應的Mapper接口-->
<mapper namespace="com.xlq.dao.UserMapper">
<!--select查詢語句-->
<select id="getUserList" resultType="user">
select * from test.user
</select>
<insert id="insertUser" parameterType="user">
insert into test.user(id, name, pwd) values (#{id}, #{name}, #{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from test.user where id=#{id}
</delete>
</mapper>
可以看到,delete語句含有語法錯誤,這是故意寫錯的。在獲取所有用戶的方法getUserList()
中有添加一個用戶,和刪除一個用戶的方法,刪除一個用戶出現錯誤,但是添加的用戶依舊被添加了,所以getUserList()
方法不是原子性的。
Spring的事務
一個使用 MyBatis-Spring 的其中一個主要原因是它允許 MyBatis 參與到 Spring 的事務管理中。而不是給 MyBatis 創建一個新的專用事務管理器,MyBatis-Spring 藉助了 Spring 中的 DataSourceTransactionManager 來實現事務管理。
要開啓 Spring 的事務處理功能,在 Spring 的配置文件中創建一個 DataSourceTransactionManager 對象,傳入的 DataSource 可以是任何能夠與 Spring 兼容的 JDBC DataSource。包括連接池和通過 JNDI 查找獲得的 DataSource。
<!--配置聲明式事務-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
Spring支持兩種事務:
- 聲明式事務
- 編程式事務
聲明式事務就是用AOP來進行橫向切入,在原有基礎上增加事務功能。編程式事務是用try-catch來捕獲異常,從而進行事務的回滾。所以我們肯定都是用聲明式事務,借用AOP來進行橫向切入。
<!--結合AOP實現事務的織入-->
<!--配置事務通知-->
<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="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事務切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.xlq.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>