1. 程式人生 > >@Transactional的一個不生效問題

@Transactional的一個不生效問題

2、瞭解一下@Transactional註解

事物的註解方式:@Transactional

@Transactional 註解的出現不足於開啟事務行為,它僅僅 是一種元資料,能夠被可以識別 @Transactional 註解所使用。

@Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private的方法上使用 @Transactional 註解,這將被忽略,也不會丟擲任何異常。

看到這裡我發現@Override註解和@Transactional註解一起使用不會導致@Transactional不生效。

3、追擊為什麼 @Transactional 註解應該只被應用到 public 方法上!!!

如下圖1:

Spring 事務實現機制(基於CglibAopProxy

@Transactional 只能應用到 public 方法才有效

只有@Transactional 註解應用到 public 方法,才能進行事務管理。這是因為在使用 Spring AOP 代理時,Spring 在呼叫在圖 1 中的 TransactionInterceptor 在目標方法執行前後進行攔截之前,DynamicAdvisedInterceptor(CglibAopProxy 的內部類)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法會間接呼叫 AbstractFallbackTransactionAttributeSource

(Spring 通過這個類獲取表 1. @Transactional 註解的事務屬性配置屬性資訊)的 computeTransactionAttribute 方法。

清單 4. AbstractFallbackTransactionAttributeSource

12345protected TransactionAttribute computeTransactionAttribute(Method method,Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;}

這個方法會檢查目標方法的修飾符是不是 public,若不是 public,就不會獲取@Transactional 的屬性配置資訊,最終會造成不會用 TransactionInterceptor 來攔截該目標方法進行事務管理。

本以為問題得到了解決,但是彬哥補充到,這個案例即使把方法的protected改為public也是不起作用的。

為什麼呢?

原因是因為@Transactional是基於springAOP代理實現的,它僅僅 是一種元資料,能夠被可以識別 @Transactional 註解和上述的配置適當的具有事務行為的beans所使用。process方法是updateParticularsCheckResult方法內部的方法,無法獲取到該bean,所以還是不會使@Transactional生效。

終上,該問題導致@Transactional失效有倆個原因,

原因1、@Transactional只能應用在public方法才有效

原因2、 @Transactional不能作用在方法的內部的任何方法上

4、補充

class C1 : 方法a1 ,方法b1

classC2:   方法a2

事務方法a1  事務方法b1   倆者事務都會執行

事務方法a1  非事務方法b1   a1事務會執行

非事務方法a1  事務方法b1   b1事務不會執行

事務方法a1  事務方法a1   倆者事務都會執行

事務方法a1  非事務方法b1   a1事務會執行

非事務方法a1  事務方法b1   b1事務會執行

其中值得深究的是 非事務方法a1  事務方法b1   b1事務不會執行  ,

原因是: 

在 Spring 的 AOP 代理下,只有目標方法由外部呼叫,目標方法才由 Spring 生成的代理物件來管理,這會造成一個自呼叫問題。

若同一類中的其他沒有@Transactional 註解的方法內部呼叫有@Transactional 註解的方法,有@Transactional 註解的方法的事務就會被忽略,不會發生回滾。

這裡需要對 Spring 生成的代理物件來管理造成一個自呼叫問題。解釋一下:

在應用系統採用了 -> 呼叫宣告@Transactional 的目標方法時,Spring Framework 預設使用 AOP 代理,在程式碼執行時生成一個代理物件,根據@Transactional 的屬性配置資訊,這個代理物件決定該宣告@Transactional 的目標方法是否由攔截器 TransactionInterceptor 來使用攔截,在 TransactionInterceptor 攔截時,會在目標方法開始執行之前建立並加入事務,並執行目標方法的邏輯;分析這句話 意思也就是要想讓spring的宣告式@Transactional 生效,必須要經過 攔截器 TransactionInterceptor處理,而自呼叫問題導致的原因也正是TransactionInterceptor沒有 攔截到。

解決方法:

根本原因是 由於使用 Spring AOP 代理造成的。所以替代Spring AOP 代理問題就解決了,

使用 AspectJ 取代 Spring AOP 代理。

 AspectJ 的 xml 配置資訊

12345678910<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean></beanclass="org.springframework.transaction.aspectj.AnnotationTransactionAspect"factory-method="aspectOf"><property name="transactionManager" ref="transactionManager" /></bean>

同時在 Maven 的 pom 檔案中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。

 AspectJ 的 pom 配置資訊

1234567891011121314151617181920212223242526272829303132<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>4.3.2.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.9</version></dependency><plugin><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.9</version><configuration><showWeaveInfo>true</showWeaveInfo><aspectLibraries><aspectLibrary><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></aspectLibrary></aspectLibraries></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions></plugin>

而且使用AspectJ 取代 Spring AOP 代理還可以解決@Transactional 註解只應用到 public 方法的問題。