天天看點

spring架構詳解系列(二)------AOP及spring事務、傳播行為相關總結

原創文章,轉載請注明出處!!!

Spring架構—面向切面程式設計(AOP)

1 什麼是AOP

  • 在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP(面向對象程式設計)的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
  • AOP采取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼(簡單概括為8個字:縱向重複,橫向抽取)

    經典應用:事務管理、性能監視、安全檢查、緩存 、日志等

  • Spring AOP使用純Java實作,不需要專門的編譯過程和類加載器,在運作期通過代理方式向目标類織入增強代碼
  • AspectJ是一個基于Java語言的AOP架構,Spring2.0開始,Spring AOP引入對Aspect的支援,AspectJ擴充了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入

例如:

實作AOP思想的有:攔截器(攔截器處理action請求,在每個action方法前或後增減操作,實作功能),過濾器(使用過濾器處理Post亂碼請求),動态代理實作事務。

2 AOP實作原理

概念:所謂spring的aop開發其實就是spring封裝了代理技術來展現aop思想.

spring架構一共封裝了兩種代理技術來展現aop思想.

接口 + 實作類 :spring采用 jdk 的動态代理Proxy。注意:動态代理需要有接口才能對類進行代理.是有局限性的.

實作類:spring 采用 cglib位元組碼增強。cglib代理原理屬于繼承式代理.如果目标類(被代理對象)可被繼承.那麼就可以使用cglib代理增強.

注意:cglib代理無法增強被final修飾的類.以為被fanal修飾,無法被繼承.

基礎知識積累:裝飾者模式

動态代理是裝飾者模式的一種延伸和封裝:

裝飾者必備的條件:實作相同的接口;含有(可擷取)目前實作類的對象;在(可調用)相應的方法,在該方法前後增減相關功能實作不同的功能。

裝飾者實作代碼如下

public class UserServiceWarpper implements UserService {
    
    	private UserService target;
    	
    	public UserServiceWarpper(UserService target) {
    		super();
    		this.target = target;
    	}
    
    	@Override
    	public void save() {
    		System.out.println("開啟事務!");
    		target.save();
    		System.out.println("事務關閉!");
    		
    	}
    
    	@Override
    	public void delete() {
    		System.out.println("開啟事務!");
    		target.delete();
    		System.out.println("事務關閉!");
    	}
    
    	@Override
    	public void update() {
    		System.out.println("開啟事務!");
    		target.update();
    		System.out.println("事務關閉!");
    	}
    
    	@Override
    	public void find() {
    		System.out.println("開啟事務!");
    		target.find();
    		System.out.println("事務關閉!");
    	}
    }
    
 //測試類
public class Demo {
	@Test
	public void fun1(){
		
		//1 建立目标對象
		UserService target = new UserServiceImpl();
		//2 建立裝飾對象
		UserService proxy = new UserServiceWarpper(target);
		//3 測試
		proxy.save();
		proxy.delete();
		
	}
}
           

詳細了解通路:https://www.jianshu.com/p/d7f20ae63186

3 AOP術語

AOP聯盟提出的AOP領域的專用名詞

1.Joinpoint(連接配接點):所謂連接配接點是指那些可能被代理(攔截)的方法。

2.PointCut 切入點:已經或即将被代理(增強)的方法(連接配接點)。例如:save()

3.advice 通知/增強,需要對切點增強代碼。例如:after、before

4.target:目标對象,需要被代理的類。例如:UserService

5.proxy 代理對象(類)

6.Weaving(織入):動詞,對目标對象中的切點應用(織入)了通知之後形成的對象;

即是指把增強advice應用到目标對象target來建立新的代理對象proxy的過程.

7.Aspect(切面): 是切入點pointcut和通知advice的結合

一個切入點和一個通知,組成成一個特殊的面。

如圖下圖所示
           
spring架構詳解系列(二)------AOP及spring事務、傳播行為相關總結

4 AOP實作方式

手動實作spring支援的兩種動态代理的代碼

1.目标類:接口 + 實作類(jdk動态代理)

public interface UserService {
	void save();
	void delete();
	void update();
	void find();
}

 //目标對象(被代理對象)
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("使用者新增!");
		//int i = 1/0;
	}
	@Override
	public void delete() {
		System.out.println("使用者删除!");
	}
	@Override
	public void update() {
		System.out.println("使用者修改!");
	}
	@Override
	public void find() {
		System.out.println("使用者查詢!");
	}
}
           

3.工廠類:編寫工廠生成代理對象

//示範動态代理實作InvocationHandler
public class UserServiceProxyFactory1 implements InvocationHandler {
	//目标對象
	private UserService target;
	
	public UserServiceProxyFactory1(UserService target) {
		super();
		this.target = target;
		
		if(this.target == null){
			throw new RuntimeException("目标對象不能為空!");
		 }
	}
	
	//建立動态代理對象并傳回
	public UserService getUserServiceProxy(){
		//參數1:類的加載器=> 加載動态代理生成的類
		//參數2:代理類需要實作的接口(們)
		//參數3:
		return (UserService) Proxy.newProxyInstance(this.getClass().getClassLoader(),
									  new Class[]{UserService.class},this);
	}
	@Override
	//參數1: 代表代理對象
	//參數2: 代表目前代理的業務方法
	//參數3: 方法執行參數
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("開啟事務");
		//調用業務方法
		Object result = method.invoke(target, args);
		System.out.println("關閉事務");
		return result;
	}	
}
//測試類
public class Demo {
	@Test
	public void fun1(){
		
		//1 建立目标對象
		UserService target = new UserServiceImpl();
		//2 建立代理工廠
		UserServiceProxyFactory1 factory = new UserServiceProxyFactory1(target);
		//3 建立代理
		UserService us = factory.getUserServiceProxy();
		//4 測試
		us.save();
		
		System.out.println(us instanceof UserServiceImpl);
	}
}
           

導入相關的包Spring-core…jar

//示範CGLIB代理
public class UserServiceProxyFactory2 implements MethodInterceptor {
	//建立cglib代理對象并傳回
	public UserService getUserServiceProxy(){
		Enhancer en = new Enhancer();
		//指定誰是目标對象 => cglib好生成子類(代理對象)
		en.setSuperclass(UserServiceImpl.class);
		//指定代理類中的方法如何實作 (類似于動态代理方法Proxy.newProxyInstance中的第三個參數)
		en.setCallback(this);
		return (UserService) en.create();
	}
	@Override
	//參數1: 代理對象
	//參數2: 目前代理的業務方法
	//參數3: 方法參數
	//參數4: 代理方法對象
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable {
		System.out.println("開啟事務");
		//調用業務方法 => 調用父類方法
		Object result = methodProxy.invokeSuper(arg0, arg2);
		System.out.println("關閉事務");
		return result;
	}
}
//測試類
public class Demo2 {
	@Test
	public void fun1(){
		
		//2 建立代理工廠
		UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
		//3 建立代理
		UserService us = factory.getUserServiceProxy();
		//4 測試
		us.save();
		
		System.out.println(us instanceof UserServiceImpl);
	}
}
           

springAOP開發流程(xml)

1.導入相關的jar包:

4+2(context,beans、core、expression+common、log4j)

spring-aop 、spring-aspects

aop聯盟 aspectsJ-weaver

junit測試包

2.準備目标類

如前文書寫UserService

3.準備通知

/通知類
public class MyAdvice {
	public void MYPC(){}
	/*
	 * 前置通知: 在切點執行之前執行的代碼 環繞通知: 在切點執行之前和之後執行的代碼 後置通知:
	 * 在切點執行之後執行的代碼.如果切點方法執行出現異常就不執行. 後置通知: 在切點執行之後執行的代碼.如果切點方法執行出現異常仍然執行. 異常通知:
	 * 出現異常後才執行通知代碼.
	 */
	public void before() {
		System.out.println("前置通知!");
	}
	
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("環繞通知!-前面部分");
		// 執行切點(業務)方法
		Object result = pjp.proceed();
		System.out.println("環繞通知!-後面部分");
		return result;
	}

	public void afterReturning() {
		System.out.println("後置通知!如果切點方法執行出現異常就不執行.");
	}


	public void after() {
		System.out.println("後置通知!如果切點方法執行出現異常仍然執行.");
	}


	public void afterThrowing() {
		System.out.println("異常通知!");
	}
}

           

4.引入Aop限制,配置切面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
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-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
	<!-- 注冊目标對象 -->
	<bean name="userService" class="com.huawei.service.UserServiceImpl" ></bean>
	<!-- 注冊通知對象 -->
	<bean name="myAdvice" class="com.huawei.spring_aop.MyAdvice" ></bean>
	<!-- 配置切面=>切點+通知 -->
	<!-- 切點表達式
			execution(表達式)
			public void com.huawei.service.UserServiceImpl.save()
			void com.huawei.service.UserServiceImpl.save() 預設就是public
			* com.huawei.service.UserServiceImpl.save() 傳回值任意
			* com.huawei.service.*ServiceImpl.save() 指定包中所有ServiceImpl結尾的類
			* com.huawei.service.*ServiceImpl.*() 方法名任意
			* com.huawei.service.*ServiceImpl.*(..) 參數清單任意
			* com.huawei.service..*ServiceImpl.*(..) 目前包以及後代包
	 -->
	<aop:config>
		<!-- 定義切點 -->
		<aop:pointcut id="myAdvice" expression="execution(*com.huawei.service.*ServiceImpl.*(..))" id="MyPC"/>
		<!-- 定義切面 -->
		<aop:aspect ref="myAdvice" >
			<!-- 前置通知 
				将myAdvice對象中的before方法作為通知織入到MyPC指定的切點(方法)
			-->
			<aop:before method="before" pointcut-ref="MyPC"/>
			<!-- 環繞通知 -->
			<aop:around method="around" pointcut-ref="MyPC"/>
			<!-- 後置通知 -->
			<aop:after-returning method="afterReturning" pointcut-ref="MyPC"/>
			<!-- 後置通知 -->
			<aop:after method="after" pointcut-ref="MyPC"/>
			<!-- 異常通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="MyPC"/>
		</aop:aspect>
	</aop:config>
</beans>
           

5.測試類

//建立spring容器 
@RunWith(SpringJUnit4ClassRunner.class)
//指定spring容器配置
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
	@Autowired
	private UserService us;
	
	@Test
	public void fun1() throws Exception{
		us.save();
	}
}
           

springAOP開發流程(注解版)

/通知類
//@Aspect表示在目前類中配置切面
@Aspect
public class MyAdvice {
	//抽取切點表達式
	@Pointcut("execution(* com.huawei.service.*ServiceImpl.*(..))")
	public void MYPC(){}
	
	/*
	 * 前置通知: 在切點執行之前執行的代碼 環繞通知: 在切點執行之前和之後執行的代碼 後置通知:
	 * 在切點執行之後執行的代碼.如果切點方法執行出現異常就不執行. 後置通知: 在切點執行之後執行的代碼.如果切點方法執行出現異常仍然執行. 異常通知:
	 * 出現異常後才執行通知代碼.
	 */
	// 前置通知
	//前置切面 => 通知+切點
	@Before("MyAdvice.MYPC()")
	public void before() {
		System.out.println("前置通知!");
	}
	// 環繞通知
	@Around("MyAdvice.MYPC()")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("環繞通知!-前面部分");
		// 執行切點(業務)方法
		Object result = pjp.proceed();
		System.out.println("環繞通知!-後面部分");
		return result;
	}
	// 後置通知
	@AfterReturning("execution(* com.huawei.service.*ServiceImpl.*(..))")
	public void afterReturning() {
		System.out.println("後置通知!如果切點方法執行出現異常就不執行.");
	}
	// 後置通知
	@After("execution* com.huawei.service.*ServiceImpl.*(..))")
	public void after() {
		System.out.println("後置通知!如果切點方法執行出現異常仍然執行.");
	}
	// 異常通知
	@AfterThrowing("execution(* com.huawei.service.*ServiceImpl.*(..))")
	public void afterThrowing() {
		System.out.println("異常通知!");
	}
}

           

配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
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-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
	<!-- 注冊目标對象 -->
	<bean name="userService" class="com.huawei.service.UserServiceImpl" ></bean>
	<!-- 注冊通知對象 -->
	<bean name="myAdvice" class="com.huawei.spring_aop.MyAdvice" ></bean>
	<!-- 開啟注解管理實務開關 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
           

測試類

//建立spring容器 
@RunWith(SpringJUnit4ClassRunner.class)
//指定spring容器配置
@ContextConfiguration("classpath:applicationContext2.xml")
public class Demo2 {
	@Autowired
	private UserService us;
	
	@Test
	public void fun1() throws Exception{
		us.save();
	}

}
           

Spring架構—事務處理及傳播行為

  • spring中事務控制位于分層開發照片那個的業務層,是為業務層事務處理提供解決方案
  • spring架構為我們提供了一組事務控制的接口,需要導入spring-tx.xx.jar
  • spring事務控制都是基于aop思想的,既可以通過程式設計的方式實作也可以通過spring配置的方式實作

spring中事務管理的API介紹

常用的操作事務的方法為PlatFormTransactionManager接口的方法:

擷取事務

TransactionStatus getTransaction(TransactionDefinition definition)

送出事務

void commit(TransactionStatus status)

復原事務

void rollback(TransactionStatus status)

TransactionDefinition 類時事務定義資訊對象的

方法有:

String getName() 擷取事務對象的名稱

Int getIsolationLevel() 擷取事務的隔開級别

Int getTimeOut() 擷取逾時時間

Int getpropagationbehavior() 擷取事務的傳播行為

開發中我們使用它實作類:

DataSourceTransactionManager 使用springJDBC或mybatis實作持久化資料庫

HitbernateTransactionManager 使用Hitbernate實作持久化資料庫

JpaTransactionManager 使用JPA實作持久化資料庫

事務的隔離級别

了解事務的4種隔離級别

資料庫事務的隔離級别有4種,由低到高分别為Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事務的并發操作中可能會出現髒讀,不可重複讀,幻讀。下面通過事例一一闡述它們的概念與聯系。

Read uncommitted

讀未送出,顧名思義,就是一個事務可以讀取另一個未送出事務的資料。

事例:老闆要給程式員發工資,程式員的工資是3.6萬/月。但是發工資時老闆不小心按錯了數字,按成3.9萬/月,該錢已經打到程式員的戶口,但是事務還沒有送出,就在這時,程式員去檢視自己這個月的工資,發現比往常多了3千元,以為漲工資了非常高興。但是老闆及時發現了不對,馬上復原差點就送出了的事務,将數字改成3.6萬再送出。

分析:實際程式員這個月的工資還是3.6萬,但是程式員看到的是3.9萬。他看到的是老闆還沒送出事務時的資料。這就是髒讀。

那怎麼解決髒讀呢?Read committed!讀送出,能解決髒讀問題。

Read committed

讀送出,顧名思義,就是一個事務要等另一個事務送出後才能讀取資料。

事例:程式員拿着信用卡去享受生活(卡裡當然是隻有3.6萬),當他埋單時(程式員事務開啟),收費系統事先檢測到他的卡裡有3.6萬,就在這個時候!!程式員的妻子要把錢全部轉出充當家用,并送出。當收費系統準備扣款時,再檢測卡裡的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務送出完)。程式員就會很郁悶,明明卡裡是有錢的…

分析:這就是讀送出,若有事務對資料進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務送出後才能讀取資料,可以解決髒讀問題。但在這個事例中,出現了一個事務範圍内兩個相同的查詢卻傳回了不同資料,這就是不可重複讀。

那怎麼解決可能的不可重複讀問題?Repeatable read !

Repeatable read

重複讀,就是在開始讀取資料(事務開啟)時,不再允許修改操作

事例:程式員拿着信用卡去享受生活(卡裡當然是隻有3.6萬),當他埋單時(事務開啟,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡裡有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。

分析:重複讀可以解決不可重複讀問題。寫到這裡,應該明白的一點就是,不可重複讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。

什麼時候會出現幻讀?

事例:程式員某一天去消費,花了2千元,然後他的妻子去檢視他今天的消費記錄(全表掃描FTS,妻子事務開啟),看到确實是花了2千元,就在這個時候,程式員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,并送出。當妻子列印程式員的消費記錄清單時(妻子事務送出),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。

那怎麼解決幻讀問題?Serializable!

Serializable 序列化

Serializable 是最高的事務隔離級别,在該級别下,事務串行化順序執行,可以避免髒讀、不可重複讀與幻讀。但是這種事務隔離級别效率低下,比較耗資料庫性能,一般不使用。

值得一提的是:大多數資料庫預設的事務隔離級别是Read committed,比如Sql Server , Oracle。Mysql的預設隔離級别是Repeatable read。

現在來看看MySQL資料庫為我們提供的四種隔離級别:

① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。

② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。

③ Read committed (讀已送出):可避免髒讀的發生。

④ Read uncommitted (讀未送出):最低級别,任何情況都無法保證。

  

存在的問題:

l 髒讀:一個線程中的事務讀到了另外一個線程中未送出的資料。

l 不可重複讀:一個線程中的事務讀到了另外一個線程中已經送出的update的資料。

l 虛讀:一個線程中的事務讀到了另外一個線程中已經送出的insert的資料。

解決:

要想避免以上現象,通過更改事務的隔離級别來避免:

l READ UNCOMMITTED 髒讀、不可重複讀、虛讀有可能發生。

l READ COMMITTED 避免髒讀的發生,不可重複讀、虛讀有可能發生。

l REPEATABLE READ 避免髒讀、不可重複讀的發生,虛讀有可能發生。

l SERIALIZABLE 避免髒讀、不可重複讀、虛讀的發生。

拓展:

級别依次升高,效率依次降低。

MySQL:預設REPEATABLE READ

ORACLE:預設READ COMMITTED

MySQL:select @@tx_isolation;//檢視目前的隔離級别

set transaction isolation level 級别;// 設定目前的事務隔離級别

詳細通路:https://blog.csdn.net/hl93hnrz/article/details/55269893

Spring事務的傳播行為

傳播行為是指業務方法平行調用時,決定被調用方法的事務應該如何處理的問題

傳播行為的常見類型和說明如下:

事務傳播行為類型 說明

PROPAGATION_REQUIRED 如果目前沒有事務,就建立一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。

PROPAGATION_SUPPORTS 支援目前事務,如果目前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY 使用目前的事務,如果目前沒有事務,就抛出異常。

PROPAGATION_REQUIRES_NEW 建立事務,如果目前存在事務,把目前事務挂起。

PROPAGATION_NOT_SUPPORTED 以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。

PROPAGATION_NEVER 以非事務方式執行,如果目前存在事務,則抛出異常。

PROPAGATION_NESTED 如果目前存在事務,則在嵌套事務内執行。如果目前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

詳細通路:https://segmentfault.com/a/1190000013341344

事務在spring中的使用

準備服務類及接口

//該方法是注解版的,不加注解則需在xml中配置相關的事務
public interface BankService {
	//轉賬
	void transfer(Integer from,Integer to,Double money);
}


//為Service中的所有業務方法都織入事務通知
@Transactional(readOnly=true,propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
public class BankServiceImpl implements BankService {

	
	private BankDao bd;
	
	@Override
	//轉賬方法
	//注解加在方法上回覆寫類上的配置
	@Transactional(readOnly=false,propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
	public void transfer(Integer from, Integer to, Double money) {
		
		bd.decreaseMoney(from, money);
		//int i = 1/0;
		bd.increaseMoney(to, money);
		
	}

	public void setBd(BankDao bd) {
		this.bd = bd;
	}
}

           

準備資料庫通路類Dao接口和實作類(此處省略接口,隻提供實作類)

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class BankDao extends JdbcDaoSupport {

	
	//給指定賬戶加錢
	public void increaseMoney(Integer to , Double money){
		
		String sql = "update t_bank set money = money+? where id = ?";
		//擷取jdbc模闆對象操作資料庫
		getJdbcTemplate().update(sql, money,to);
	}
	//給指定賬戶扣錢
	public void decreaseMoney(Integer from , Double money){
		
		String sql = "update t_bank set money = money-? where id = ?";
		
		getJdbcTemplate().update(sql, money,from);
	}
	
}

           

配置xml檔案天劍事務管理器和資料源

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
 http://www.springframework.org/schema/aop 
 http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
    
     
    <!-- 讀取db.properties -->
    <context:property-placeholder location="classpath:db.properties" />
    
    <!-- c3p0連接配接池 -->
    <!-- ${jdbc.jdbcUrl} => 根據鍵引用properties中對應的值 -->
    <bean name="dataSouce" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
    	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
    	<property name="driverClass" value="${jdbc.driverClass}" ></property>
    	<property name="user" value="${jdbc.user}" ></property>
    	<property name="password" value="${jdbc.password}" ></property>
    </bean>
    <!-- 配置事務管理器 -->
    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
    	<!-- 引入資料源,關聯資料源 -->
    	<property name="dataSource" ref="dataSouce" ></property>
    </bean>
    <!-- 一、配置注解事務管理 -->
	    <!-- 開啟注解事務開關 -->
	    <tx:annotation-driven transaction-manager="transactionManager" />
    <!-- 二、配置事務通知和切面,若使用注解則可不配置 -->
      <!-- 配置事務通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager" >
    	<tx:attributes>
    		<!-- 為方法指定事務屬性 
    				name:方法名
    					save* 所有save開頭的方法
    				read-only:是否隻讀
    				isolation:隔離級别
    				propagation:傳播行為
    		-->
    		<tx:method name="save*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="delete*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="update*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="find*"  read-only="true" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    	</tx:attributes>
    </tx:advice>
    <!-- 配置事務切面 -->
    <aop:config>
    	<!-- 切點 -->
    	<aop:pointcut expression="execution(* com.huawei.service.*ServiceImpl.*(..))" id="myPC"/>
    	<!-- 切面 -->
    	<aop:advisor advice-ref="txAdvice" pointcut-ref="myPC" />
    	<aop:aspect>
    	</aop:aspect>
    </aop:config>
    <!-- 配置Dao -->
     <bean name="bd" class="com.huawei.dao.BankDao" >
     	<property name="dataSource" ref="dataSouce" ></property>
    </bean>
    
    <!-- 配置Service  -->
    <bean name="bs" class="com.huawei.service.BankServiceImpl" >
    	<property name="bd" ref="bd" ></property>
    </bean>
</beans>
           

db.properties檔案

jdbc.jdbcUrl=jdbc:mysql:///hibernate_test
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=1234
           

以上配置事務時配置的事務屬性和事務的傳播行為。

事務屬性如下

readonly:隻讀,取值true/false(查詢時可設該屬性為true)

isolation:隔離級别

有四個取值1 讀未送出 2 讀已送出 4可重複讀 8串行化

propergation 傳播行為 有7個值

timeout 逾時時間

rollback-for 為xxx異常復原 , 配置完整類名

no-rollback-for 不為xxx異常復原, 配置完整類名

測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:com/huawei/spring_aop_tx/applicationContext.xml")
public class Demo {
	@Autowired
	private BankService bs;
	
	@Test
 public void fun2() throws Exception{
		bs.transfer(1, 2, 100d);
	}
}
注解版的服務如上實作類BankServiceImpl所示
           

參考文章:

事務傳播行為詳解:https://segmentfault.com/a/1190000013341344

事務了解:https://www.cnblogs.com/xiarongjin/p/8405510.html