分析spring事務@Transactional注解在同一個類中的方法之間調用不生效的原因及解決方案


問題:

在Spring管理的項目中,方法A使用了Transactional注解,試圖實現事務性。但當同一個class中的方法B調用方法A時,會發現方法A中的異常不再導致回滾,也即事務失效了。

當這個方法被同一個類調用的時候,spring無法將這個方法加到事務管理中。

我們來看一下生效時候和不生效時候調用堆棧日志的對比。

 
 

通過對比兩個調用堆棧可以看出,spring的@Transactional事務生效的一個前提是進行方法調用前經過攔截器TransactionInterceptor,也就是說只有通過TransactionInterceptor攔截器的方法才會被加入到spring事務管理中,查看spring源碼可以看到,在AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice方法中會從調用方法中獲取@Transactional注解,如果有該注解,則啟用事務,否則不啟用。

 
 

這個方法是通過spring的AOP類CglibAopProxy的內部類DynamicAdvisedInterceptor調用的,而DynamicAdvisedInterceptor繼承了MethodInterceptor,用於攔截方法調用,並從中獲取調用鏈。

如果是在同一個類中的方法調用,則不會被方法攔截器攔截到,因此事務不會起作用,必須將方法放入另一個類,並且該類通過spring注入。

原因:

Transactional是Spring提供的事務管理注解。

重點在於,Spring采用動態代理(AOP)實現對bean的管理和切片,它為我們的每個class生成一個代理對象。只有在代理對象之間進行調用時,可以觸發切面邏輯

而在同一個class中,方法B調用方法A,調用的是原對象的方法,而不通過代理對象。所以Spring無法切到這次調用,也就無法通過注解保證事務性了。

 也就是說,在同一個類中的方法調用,則不會被方法攔截器攔截到,因此事務不會起作用。

 

解決方法1:

將事務方法放到另一個類中(或者單獨開啟一層,取名“事務層”)進行調用,即符合了在對象之間調用的條件。

解決方法2:

獲取本對象的代理對象,再進行調用。具體操作如:

1) Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy="true"/>

2) 在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),獲取到xxxService的代理類,再調用事務方法,強行經過代理類,激活事務切面。

解決方法3:

很多時候,方法內調用又希望激活事務,是由於同一個方法既有DAO操作又有I/O等耗時操作,不想讓耗時的I/O造成事務的太長耗時(比如新增商品同時需要寫入庫存)。此時,可以將I/O做成異步操作(如加入線程池),而加入線程池的操作即便加入事務也不會導致事務太長,問題可以迎刃而解。
解決方法4:

用@Autowired 注入自己 然后在用注入的bean調用自己的方法也可以

 

參考:
https://blog.csdn.net/ligeforrent/article/details/79996797

https://www.jianshu.com/p/2e4e1007edf2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM