最近在工作中遇到使用事務。一直使用的都是註解式的事務@Transactional
。完成開發自測時發現事務失效了。不科學啊,我寫的代碼怎麼可能有bug!!項目我都搭建過好幾個了,一直都是同一個套路,不可能有事務配置錯誤。於是乎分析了一下,把原因貼出來給大家共享一下。工作中肯定有踩到坑的同學。
首先說一下我的代碼結構
// 父類
public abstract class AbstractParentClass {
public void doSomething(){
// 1.一些通用邏輯1xxxxx
// 2.調用抽象方法
abstractMethod();
// 3.一些通用邏輯2xxxxx
}
// 子類實現的抽象方法
abstract void abstractMethod();
}
// 子類
public class Testclass extends AbstractParentClass {
// 子類實現方法
public void abstractMethod(){
// 1.業務邏輯1
// 2.調用內部方法
methodA();
// 3.業務邏輯2
}
private void methodA(){
}
}
類的關係式是存在兩個類,父類封裝了一些通用邏輯,暴露了一個抽象方法給子類去實現。
子類對抽象方法進行了實現,方法內部又調用了自己的若干方法。
以上是代碼結構關係的介紹。相信這種設計在工作中很常見。我的情景是在子類的methodA
方法中有需要加事務的操作。我就很果斷的在方法methodA
上加了事務註解@Transactional
。
有經驗的小夥伴可能立馬就能發現一個問題,方法methodA
是private
的,事務不會生效。這是一個錯誤。我開始也是把private
修改成了public
,但是仍然不行。
問題在哪裏呢?想一下,我們的事務是如何實現的?AOP。
比喻一下,既然是AOP的話,那得有一個觸發機制,當切面外部調用到實現類具體的方法時,穿過了切面,出發了事務。而我上面的代碼是Testclass
方法abstractMethod
調用了自己的方法methodA
,沒有經過切面,所以沒有觸發事務。這個比喻我覺得應該有助於理解。
專業一點解釋的話呢,大家都叫這是一個AOP的缺陷。當類內部方法相互調用的時候,是不會觸發AOP的。那應該怎麼做呢,子類做如下修改:
// 子類
public class Testclass extends AbstractParentClass {
// 子類實現方法
public void abstractMethod(){
// 1.業務邏輯1
// 2.調用內部方法
// methodA();
// 調用方式修改爲這樣
// 這個方法能獲取到aop對象還有一個前提,就是使用了aop。例如@Transactional加到了一個private方法上,那麼是會報錯的.而且其他配置也要配對
((Testclass)AopContext.currentProxy()).methodA();
// 3.業務邏輯2
}
@Transactional
public void methodA(){
}
}
記得要在xml配置中配置
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
問題根源是類內部不加事務的方法調用加了事務的方法導致的。其他類似的方法調用關係導致的AOP不生效問題可以以此類推。
關於AopContext.currentProxy()的詳細使用和配置可以單獨去查找,資料很多。