參考:https://blog.csdn.net/qq_30336433/article/details/83338835
最近在開發項目中踩到一個坑,以此記錄下來。以備后來人借鑒
1、相信使用spring開發的小伙伴對@Transaction這個注解應該不會陌生。
spring提供了非常強大的事務管理機制,之前一直以為只要在方法上加上@Transaction就萬事大吉了
但是最近發現有些情況下 這個注解會失效。
當這個方法被同一個類調用的時候,spring無法將這個方法加到事務管理中。
下面我們來看看為什么會失效?
其實 spring的@Transactional事務生效的一個前提是方法調用前經過攔截器TransactionInterceptor , 也就是說只有通過TransactionInterceptor攔截器的方法才會被加入到Spring事務管理中,
查看spring源碼可以知道,在AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice方法中會調用方法中獲取@Transactional注解,如果有該注解則啟用事務,否則不啟用注解
這個方法是通過spring的AOP類CglibAopProxy的內部類DynamicAdviseInterceptor調用的,而DynamicAdvisedInterceptor繼承了MethodInterceptor,用於攔截方法調用,並從中獲取調用鏈。
如果是在同一個類中的方法調用,則不會被方法攔截器攔截到,因此事務不會起作用,必須將方法放入另外一個類中,並且該類通過spring注入
總結一下:Transactional是Spring提供的事務管理注解
spring 采用動態代理(AOP)實現對Bean的管理和切片,它為我們的每個class生成一個代理對象,只有在代理對象之間進行調用時,可以觸發切面邏輯。
而在同一個類中,方法B調用A,調用的事元對象的方法,而不是通過代理對象,所以spring無法切到這次調用,也就是無法通過注解保證事務性。
解決方案:
- 可以將方法放入另一個類,並且該類通過spring注入,即符合了在對象之間調用的條件。
- 獲取本對象的代理對象,再進行調用。具體操作如:
1)Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
2)在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),獲取到xxxService的代理類,再調用事務方法,強行經過代理類,激活事務切面。 - 很多時候,方法內調用又希望激活事務,是由於同一個方法既有DAO操作又有I/O等耗時操作,不想讓耗時的I/O造成事務的太長耗時(比如新增商品同時需要寫入庫存)。此時,可以將I/O做成異步操作(如加入線程池),而加入線程池的操作即便加入事務也不會導致事務太長,問題可以迎刃而解。
???
這條不太懂