探討Spring中Transactional 失效的解決方案

關於@Transactional註解 通常都認爲要注意如下三點:

1 .在須要事務管理的地方加@Transactional 註解。@Transactional 註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上 。編程

2 . @Transactional 註解只能應用到 public 可見度的方法上 。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。測試

3 . 注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據。必須在配置文件中使用配置元素,才真正開啓了事務行爲。ui

最近在項目中發現註解無效,通過跟蹤源代碼發現了問題,因而在網上找到相同出現此問題的人,如下爲原文,講解的很詳細:代理

只要避開Spring目前的AOP實現上的限制,要麼都聲明要事務,要麼分開成兩個類,要麼直接在方法裏使用編程式事務cdn

[問題]

Spring的聲明式事務,我想就不用多介紹了吧,一句話「自從用了Spring AOP啊,事務管理真輕鬆啊,真輕鬆;事務管理代碼沒有了,腦不酸了,手不痛了,一口氣全配上了事務;輕量級,測試起來也簡單,嘿!」。無論從哪一個角度看,輕量級聲明式事務都是一件解放生產力的大好事。因此,咱們「一直用它」。對象

不過,最近的一個項目裏,卻碰到了一個事務管理上的問題:有一個服務類,其一個聲明瞭事務的方法,裏面作了三次插入SQL操做,可是在後面出錯回滾時,卻發現前面插入成功了,也是說,這個聲明瞭事務的方法,實際上並無真正啓動事務!怎麼回事呢?難道Spring的聲明式事務失效了?blog

[探幽]

其實之前也會碰到有人說,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都在一個事務裏了。

好了,如今總結一下現象:

一、ServiceA類爲Web層的Action服務

二、Action調用了ServiceA的方法A,而方法A沒有聲明事務(緣由是方法A自己比較耗時而又不須要事務)

三、ServiceA的方法A調用了本身所在class的方法B,而方法B聲明瞭事務,可是方法B的事務聲明在這種狀況失效了。

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

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

這個問題,表面上是事務聲明失效的問題,實質上極可能是Spring的AOP機制實現角度的問題。

我想到好久之前研究Spring的AOP實現時發現的一個現象:對於以Cglib方式加強的AOP目標類,會建立兩個對象,一個事Bean實例自己,一個是Cglib加強代理對象,而不只僅是隻有後者。我曾經疑惑過這一點,但當時沒有再仔細探究下去。

咱們知道,Spring的AOP實現方式有兩種:一、Java代理方式;二、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。

最後

你們以爲不錯能夠點個贊在關注下,之後還會分享更多文章!

相關文章
相關標籤/搜索