問題:
在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