spring聲明式事務 同一類內方法調用事務失效


[問題]

有一個服務類,其一個聲明了事務的方法,里面做了三次插入SQL操作,但是在后面出錯回滾時,卻發現前面插入成功了,也是說,這個聲明了事務的方法,實際上並沒有真正啟動事務!怎么回事呢?難道Spring的聲明式事務失效了?

      

[探幽]

     其實以前也會碰到有人說,Spring的事務配置不起作用,但是根據第一反應和以往經驗,我總會告訴他,肯定是你的配置有問題啦;所以這一次,我想也不會例外,大概是把事務注解配在了接口上而不是實現方法上,或者,如果是用XML聲明方式的話,很可能是切入點的表達式沒有配對。

 

     不過,在檢查了他們的配置后,卻發現沒有配置問題,該起事務的實現方法上,用了@Transactional事務注解聲明,XML里也配了注解驅動<tx:annotation-driven .../>,配置很正確啊,怎么會不起作用?

 

     我很納悶,於是往下問:

     問1:其他方法有這種情況么?

     答1:沒有。

     問2:這個方法有什么特別的么(以下簡稱方法B)?

     答2:就是調后台插了三條記錄啊,沒啥特別的。

     問3:這個方法是從Web層直接調用的吧?

     答3:不是,是這個Service類(以下簡稱ServiceA)的另外一個方法調過來的(以下簡稱方法A)。

     問4:哦,那個調用它的方法配了事務么(問題可能在這了)?

     答4:沒有。

     問5:那WEB層的Action(用的是Struts2),調用的是沒有聲明事務的方法A,方法A再調用聲明了事務的方法B?

     答5:對的。

     問6:你直接在方法A上加上事務聲明看看

     答6:好。。。

     

     看來可能找到問題所在了,於是把@Transactional也加在方法A上,啟動項目測試,結果是:事務正常生效,方法A和方法B都在一個事務里了。

 

     好了,現在總結一下現象:

     1、ServiceA類為Web層的Action服務

     2、Action調用了ServiceA的方法A,而方法A沒有聲明事務(原因是方法A本身比較耗時而又不需要事務)

     3、ServiceA的方法A調用了自己的方法B,而方法B聲明了事務,但是方法B的事務聲明在這種情況失效了。

     4、如果在方法A上也聲明事務,則在Action調用方法A時,事務生效,而方法B則自動參與了這個事務。 

 

       我讓他先把A也加上事務聲明,決定回來自己再測一下。

     

       這個問題,表面上是事務聲明失效的問題,實質上很可能是Spring的AOP機制實現角度的問題。我想到很久以前研究Spring的AOP實現時發現的一個現象:對於以Cglib方式增強的AOP目標類,會創建兩個對象,一個事Bean實例本身,一個是Cglib增強代理對象,而不僅僅是只有后者。我曾經疑惑過這一點,但當時沒有再仔細探究下去。

     

      我們知道,Spring的AOP實現方式有兩種:1、Java代理方式;2、Cglib動態增強方式,這兩種方式在Spring中是可以無縫自由切換的。Java代理方式的優點是不依賴第三方jar包,缺點是不能代理類,只能代理接口。

 

      Spring通過AopProxy接口,抽象了這兩種實現,實現了一致的AOP方式:

現在看來,這種抽象同樣帶了一個缺陷,那就是抹殺了Cglib能夠直接創建普通類的增強子類的能力,Spring相當於把Cglib動態生成的子類,當普通的代理類了,這也是為什么會創建兩個對象的原因。下圖顯示了Spring的AOP代理類的實際調用過程:

 

 

 

因此,從上面的分析可以看出,methodB沒有被AopProxy通知到,導致最終結果是:被Spring的AOP增強的類,在同一個類的內部方法調用時,其被調用方法上的增強通知將不起作用。

    

      而這種結果,會造成什么影響呢:

      1:內部調用時,被調用方法的事務聲明將不起作用

      2:換句話說,你在某個方法上聲明它需要事務的時候,如果這個類還有其他開發者,你將不能保證這個方法真的會在事務環境中

      3:再換句話說,Spring的事務傳播策略在內部方法調用時將不起作用。不管你希望某個方法需要單獨事務,是RequiresNew,還是要嵌套事務,要Nested,等等,統統不起作用。

      4:不僅僅是事務通知,所有你自己利用Spring實現的AOP通知,都會受到同樣限制。。。。

 

[解難]

     

      問題的原因已經找到,其實,我理想中的AOP實現,應該是下面這樣:

 

只要一個Cglib增強對象就好,對於Java代理方式,我的選擇是毫不猶豫的拋棄。

 

      至於前面的事務問題,只要避開Spring目前的AOP實現上的限制,要么都聲明要事務,要么分開成兩個類,要么直接在方法里使用編程式事務,那么一切OK。

 

原文地址:http://blog.csdn.net/seelye/article/details/40144817


免責聲明!

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



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