從Spring AOP的原理理解@Transactional失效問題

轉(zhuǎn)自:https://blog.csdn.net/canot/article/details/80855439磅氨,如有侵權(quán)醋奠,請聯(lián)系我刪除鳍寂,謝謝

在正確配置了Spring事務(wù)管理后搔确,或許在某些場景下杆烁,你可以寫出如下代碼:

class T {

    public int createFirst(){
       //dosometing....
       try {
           this.createSecond();
       }catch (Exception e){
          throw e;
       }
       //dosometing....
       return 0;
    }


    @Transactional
    public int createSecond(){
       //dosometing with db....

    }
}

然后在外部通過Spring容器獲取T的實例t陪竿,當(dāng)你調(diào)用t.createFirst()方法的時候禽翼,你會發(fā)現(xiàn)添加了@Transactional注解的createSecond()方法并沒有運行在事務(wù)中。
為了了解這個問題的原理族跛,首先先需要了解Spring 是如何處理注解的(@Transaction 或 @Async)瓮下。

AOP
面向切面編程妇菱,通俗一點將即是在要執(zhí)行的方法前或后執(zhí)行一段代碼。類使用Spring MVC中的過濾器,在請求前后執(zhí)行額外的邏輯漠秋,并且該邏輯對實際的方法是透明的祷肯。

AOP的實現(xiàn)原理

  1. 編譯時織入:在代碼編譯時萨西,把切面代碼融合進來芙代,生成完整功能的Java字節(jié)碼,這就需要特殊的Java編譯器了茉继,AspectJ屬于這一類
  2. 類加載時織入:在Java字節(jié)碼加載時咧叭,把切面的字節(jié)碼融合進來,這就需要特殊的類加載器烁竭,AspectJ和AspectWerkz實現(xiàn)了類加載時織入
  3. 運行時織入:在運行時菲茬,通過動態(tài)代理的方式,調(diào)用切面代碼增強業(yè)務(wù)功能,Spring采用的正是這種方式婉弹。動態(tài)代理會有性能上的開銷睬魂,但是好處就是不需要神馬特殊的編譯器和類加載器啦,按照寫普通Java程序的方式來就行了马胧!

Spring AOP 屬于運行時織入汉买,即使用代理模式∨寮梗或許你看到Spring AOP依賴了AspectJ包會以為Spring AOP是AspectJ來實現(xiàn)的。但其他并不是垫卤,Spring AOP只是使用了和AspectJ一樣的注解但并沒有使用 AspectJ 的編譯器或者織入器(Weaver)威彰,底層AOP實現(xiàn)與AspectJ是不同的。Spring AOP 無需使用任何特殊命令對 Java 源代碼進行編譯穴肘,它采用運行時動態(tài)地歇盼、在內(nèi)存中臨時生成“代理類”的方式來生成 AOP 代理。

Spring AOP的代理實現(xiàn)

1.JDK動態(tài)代理:JDK動態(tài)代理技術(shù)评抚。通過需要代理的目標(biāo)類的getClass().getInterfaces()方法獲取到接口信息(這里實際上是使用了Java反射技術(shù)豹缀。getClass()和getInterfaces()函數(shù)都在Class類中,Class對象描述的是一個正在運行期間的Java對象的類和接口信息)慨代,通過讀取這些代理接口信息生成一個實現(xiàn)了代理接口的動態(tài)代理Class(動態(tài)生成代理類的字節(jié)碼)邢笙,然后通過反射機制獲得動態(tài)代理類的構(gòu)造函數(shù),并利用該構(gòu)造函數(shù)生成該Class的實例對象(InvokeHandler作為構(gòu)造函數(shù)的入?yún)鬟f進去)侍匙,在調(diào)用具體方法前調(diào)用InvokeHandler來處理氮惯。
2.CGLib動態(tài)代理:字節(jié)碼技術(shù)。利用asm開源包想暗,把代理對象類的class文件加載進來妇汗,通過修改其字節(jié)碼生成子類來處理。采用非常底層的字節(jié)碼技術(shù)说莫,為一個類創(chuàng)建子類杨箭,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,并順勢織入橫切邏輯储狭。

在我們實際使用Spring AOP時互婿,它包括基于XML配置的AOP和基于@AspcetJ注解的AOP,這兩種方法雖然在配置切面時的表現(xiàn)方式不同晶密,但底層都是使用動態(tài)代理技術(shù)(JDK代理或CGLib代理)擒悬。

現(xiàn)在回頭來看本文開頭的代碼
createFirst()方法里面直接調(diào)用createSecond(……)方法,這里還隱含一個關(guān)鍵字稻艰,那就是this懂牧,實際上這里調(diào)用是這樣的:this.createSecond(),this是當(dāng)前對象。當(dāng)前對象是T,問題就出在這里僧凤,因為要想用事務(wù)執(zhí)行createSecond(……)畜侦,必須用代理對象執(zhí)行,因為代理對象會攔截到@Transactional來執(zhí)行相關(guān)的增強躯保,但是此時卻直接用T對象調(diào)用旋膳,繞過了代理對象增強的部分,也就是說代理增強部分失效途事,@Transactional注解失效验懊。

如果在外部通過Spring容器獲取T實例,然后直接調(diào)用createSecond方法尸变,此時createSecond()是被代理的义图,在代理對象中判斷到該方法有@Transactional,則會執(zhí)行該注解需要的前置增強后(開啟事務(wù))召烂,然后通過invoke碱工,用實際T對象來調(diào)用addOrder()方法執(zhí)行業(yè)務(wù)邏輯,然后執(zhí)行后置增強(回滾or提交)奏夫。

特殊場景的解決辦法
沒有用代理對象執(zhí)行createSecond(……)怕篷,被T對象搶占了先機。那么解決就是要讓代理對象來執(zhí)行createSecond(……)酗昼。

T t = (T) AopContext.currentProxy(); 
//獲取代理對象
t.createSecond(); 
//通過代理對象調(diào)用createSecond

//需要在@EnableAspectJAutoProxy添加屬性值廊谓。
//@EnableAspectJAutoProxy(exposeProxy = true)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仔雷,隨后出現(xiàn)的幾起案子蹂析,更是在濱河造成了極大的恐慌,老刑警劉巖碟婆,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異竖共,居然都是意外死亡蝙叛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進店門公给,熙熙樓的掌柜王于貴愁眉苦臉地迎上來借帘,“玉大人,你說我怎么就攤上這事淌铐》稳唬” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵腿准,是天一觀的道長际起。 經(jīng)常有香客問我拾碌,道長,這世上最難降的妖魔是什么街望? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任校翔,我火速辦了婚禮,結(jié)果婚禮上灾前,老公的妹妹穿的比我還像新娘防症。我一直安慰自己,他們只是感情好哎甲,可當(dāng)我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布蔫敲。 她就那樣靜靜地躺著,像睡著了一般炭玫。 火紅的嫁衣襯著肌膚如雪燕偶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天础嫡,我揣著相機與錄音,去河邊找鬼酝惧。 笑死榴鼎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晚唇。 我是一名探鬼主播巫财,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哩陕!你這毒婦竟也來了平项?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤悍及,失蹤者是張志新(化名)和其女友劉穎闽瓢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體心赶,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡扣讼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缨叫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椭符。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耻姥,靈堂內(nèi)的尸體忽然破棺而出销钝,到底是詐尸還是另有隱情,我是刑警寧澤琐簇,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布蒸健,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏纵装。R本人自食惡果不足惜征讲,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望橡娄。 院中可真熱鬧诗箍,春花似錦、人聲如沸挽唉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汤求,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間严拒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工挤牛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留种蘸,地道東北人。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓诫硕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親痘括。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,499評論 2 348