@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
清單 4. AbstractFallbackTransactionAttributeSource
12345 | protected 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 > </ bean class = "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 方法的問題。