亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

【Spring】一次線上@Transational事務(wù)注解未生效的原因探究

姘存按 / 2535人閱讀

摘要:由于的限制,無法替換被代理類已經(jīng)被載入的字節(jié)碼,只能生成并載入一個新的子類作為代理類,被代理類的字節(jié)碼依然存在于中。區(qū)別于前兩者,是一種靜態(tài)代理的實現(xiàn),即在編譯時或者載入類時直接修改被代理類文件的字節(jié)碼,而非運行時實時生成代理。

現(xiàn)象描述

上周同事發(fā)現(xiàn)其基于mySql實現(xiàn)的分布式鎖的線上代碼存在問題,代碼簡化如下:

@Controller
class XService {
    @Autowired
    private YService yService;
    public void doOutside(){
        this.doInside(); //或者直接doInside();效果是一樣的
    }
    @Transactional
    private void doInside(){
        //do sql statement
    }
}
@Controller
class Test {
    @Autowired
    private XService xService;
    public void test(){
        xService.doOutside();
    }
}

實際執(zhí)行test()后發(fā)現(xiàn)doInside()的Sql執(zhí)行過程沒有被Spring Transaction Manager管理起來。

發(fā)現(xiàn)的兩個問題

在一個實例方法中調(diào)用被@Transactional注解標記的另一個方法,且兩個方法都屬于同一個類時,事務(wù)不會生效。

調(diào)用被@Transactional注解標記的非public方法,事務(wù)不會生效。

首先復習下相關(guān)知識:Spring AOP、JDK動態(tài)代理、CGLIB、AspectJ、@Aspect

@Transactional的實現(xiàn)原理是在業(yè)務(wù)方法外邊通過Spring AOP包上一層事務(wù)管理器的代碼(即插入切面),這是Java設(shè)計模式中常見的通過代理增強被代理類的做法。

Spring AOP的底層有2種實現(xiàn):JDK動態(tài)代理、CGLIB。前者的原理是JDK反射,并且只支持Java接口的代理;后者的原理是繼承(extend)與覆寫(override),因此能支持普通的Java類的代理。兩種方式都是動態(tài)代理,即運行時實時生成代理。

由于JVM的限制,CGLIB無法替換被代理類已經(jīng)被載入的字節(jié)碼,只能生成并載入一個新的子類作為代理類,被代理類的字節(jié)碼依然存在于JVM中。

區(qū)別于前兩者,AspectJ是一種靜態(tài)代理的實現(xiàn),即在編譯時或者載入類時直接修改被代理類文件的字節(jié)碼,而非運行時實時生成代理。因此這種方式需要額外的編譯器或者JVM Agent支持,通過一些配置Spring和AspectJ也可以配合使用。

@Aspect一開始是AspectJ推出的Java注解形式,后來Spring AOP也支持使用這種形式表示切面,但實際上底層實現(xiàn)和AspectJ毫無關(guān)系,畢竟Spring AOP是動態(tài)代理,和靜態(tài)代理是不兼容的。

進一步分析

既然事務(wù)管理器沒有生效,那么首先需要確定一個問題:this到底是指向哪個對象,是未增強的XService還是增強后的XService?并且而且有沒有可能已經(jīng)調(diào)用增強后的實例和方法,但由于其他原因而導致事務(wù)管理器沒有生效?

回憶下Java基礎(chǔ),this表示的是類的當前實例,那么關(guān)鍵就是確定類的實例是未被增強的XService(下面稱其為XService),還是被CGLIB增強過的XService(下面稱其為XService$$Cglib)。

在Test中,XService類的實例變量是一個由Spring框架管理的Bean,當執(zhí)行test()時,根據(jù)@Autowired注解進行相應的注入,因此XService的實例實際為XService$$Cglib而不XService。被增強過的類的代碼可以簡化如下:

class XService$$Cglib extend XService {
    @Override
    public doInside(){
        //開始事務(wù)的增強代碼
        super.doInside();
        //結(jié)束事務(wù)的增強代碼
    }
}

當執(zhí)行XService$$Cglib.doOutside()時,由于子類沒有覆寫父類同名方法,因此實際上執(zhí)行了父類XServicedoOutside()方法,所以在執(zhí)行其this.doInside()時實際上調(diào)用的是父類未增強過的doInside(),因此事務(wù)管理器失效了。

這個問題在Spring AOP中廣泛存在,即自調(diào)用,本質(zhì)上是動態(tài)代理無法解決的盲區(qū),只有AspectJ這類靜態(tài)代理才能解決。

第二個問題則是Spring AOP不支持非public方法增強,與自調(diào)用類似,也是動態(tài)代理無法解決的盲區(qū)。

雖然CGLIB通過繼承的方式是可以支持public、protected、package級別的方法增強的,但是由于JDK動態(tài)代理必須通過Java接口,只能支持public級別的方法,因此Spring AOP不得不取消非public方法的支持。

“自調(diào)用”的解決方法 1. 最好在被代理類的外部調(diào)用其方法 2. 自注入(Self Injection, from Spring 4.3)
@Controller
class XService {
    @Autowired
    private YService yService;
    @Autowired
    private XService xService;
    public void doOutside(){
        xService.doInside();//從this換成了xService
    }
    @Transactional
    private void doInside(){
        //do sql statement
    }
}
@Controller
class Test {
    @Autowired
    private XService xService;
    public void test(){
        xService.doOutside();
    }
}

由于xService變量是被Spring注入的,因此實際上指向XService$$Cglib對象,xService.doInside()因此也能正確的指向增強后的方法。

一種錯誤的解決辦法:改造為Java接口的形式
@Controller
class XService implements IXService {
    @Autowired
    private YService yService;
    @Override
    public void doOutside(){
        this.doInside();
    }
    @Transactional
    private void doInside(){
        //do sql statement
    }
}
@Controller
class Test {
    @Autowired
    private IXService iXService;
    public test(){
        iXService.doOutside();
    }
}

原因是之前錯誤地理解事務(wù)未生效的原理:如果沒有在xml中要設(shè)置只用CGLIB,@Transactional只能使用JDK動態(tài)代理,所以如果沒有用Java接口方式進行代理就不會生效。

實際上,這還是避免不了自調(diào)用的問題,因為這是動態(tài)代理的普遍問題,無論是JDK動態(tài)代理還是CGLIB動態(tài)代理。

總結(jié)

使用Spring AOP的時候一定要小心,如果是使用注解形式聲明AOP,要保證在被代理類的外部調(diào)用被增強的方法。

Reference

Spring AOP 實現(xiàn)原理與 CGLIB 應用

關(guān)于spring的aop攔截的問題 protected方法代理問題

透徹的掌握 Spring 中@transactional 的使用

Spring @Transactional原理及使用

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/67694.html

相關(guān)文章

  • 一次線上問題排查解決過程

    摘要:排查異常日志,發(fā)現(xiàn)沒有該問題存在。測試功能正常,沒有重現(xiàn)線上問題。解決問題原因定位好了,剩下的就是如何解決了。兩個方案修改線上配置該上實施難度系數(shù)高,因為公司使用的統(tǒng)一發(fā)布部署平臺,開發(fā)人員無服務(wù)器操作權(quán)限。 問題 XX系統(tǒng)中,一個用戶需要維護的項目數(shù)過多,填寫的任務(wù)數(shù)超多,產(chǎn)生了一次工時保存中,只有前面一部分的xx數(shù)據(jù)持久化到數(shù)據(jù)庫,后面的數(shù)據(jù)沒有保存。 圖1 showImg(htt...

    宋華 評論0 收藏0
  • 一次線上問題排查解決過程

    摘要:排查異常日志,發(fā)現(xiàn)沒有該問題存在。測試功能正常,沒有重現(xiàn)線上問題。解決問題原因定位好了,剩下的就是如何解決了。兩個方案修改線上配置該上實施難度系數(shù)高,因為公司使用的統(tǒng)一發(fā)布部署平臺,開發(fā)人員無服務(wù)器操作權(quán)限。 問題 XX系統(tǒng)中,一個用戶需要維護的項目數(shù)過多,填寫的任務(wù)數(shù)超多,產(chǎn)生了一次工時保存中,只有前面一部分的xx數(shù)據(jù)持久化到數(shù)據(jù)庫,后面的數(shù)據(jù)沒有保存。 圖1 showImg(htt...

    airborne007 評論0 收藏0
  • 一次線上問題排查解決過程

    摘要:排查異常日志,發(fā)現(xiàn)沒有該問題存在。測試功能正常,沒有重現(xiàn)線上問題。解決問題原因定位好了,剩下的就是如何解決了。兩個方案修改線上配置該上實施難度系數(shù)高,因為公司使用的統(tǒng)一發(fā)布部署平臺,開發(fā)人員無服務(wù)器操作權(quán)限。 問題 XX系統(tǒng)中,一個用戶需要維護的項目數(shù)過多,填寫的任務(wù)數(shù)超多,產(chǎn)生了一次工時保存中,只有前面一部分的xx數(shù)據(jù)持久化到數(shù)據(jù)庫,后面的數(shù)據(jù)沒有保存。 圖1 showImg(htt...

    HollisChuang 評論0 收藏0
  • Java進階之路

    摘要:探索專為而設(shè)計的將探討進行了何種改進,以及這些改進背后的原因。關(guān)于最友好的文章進階前言之前就寫過一篇關(guān)于最友好的文章反響很不錯,由于那篇文章的定位就是簡單友好,因此盡可能的摒棄復雜的概念,只抓住關(guān)鍵的東西來講,以保證大家都能看懂。 周月切換日歷 一個可以進行周月切換的日歷,左右滑動的切換月份,上下滑動可以進行周,月不同的視圖切換,可以進行事件的標記,以及節(jié)假日的顯示,功能豐富 Andr...

    sushi 評論0 收藏0
  • 面試分享:最全Spring事務(wù)面試考點整理

    摘要:和事務(wù)的關(guān)系關(guān)系型數(shù)據(jù)庫某些消息隊列等產(chǎn)品或中間件稱為事務(wù)性資源,因為它們本身支持事務(wù),也能夠處理事務(wù)。事務(wù)的傳播特性,,,,,,強制要求要有一個物理事務(wù)。外圍事務(wù)不會被內(nèi)部事務(wù)的回滾狀態(tài)影響。不支持當前事務(wù)。 Spring和事務(wù)的關(guān)系 關(guān)系型數(shù)據(jù)庫、某些消息隊列等產(chǎn)品或中間件稱為事務(wù)性資源,因為它們本身支持事務(wù),也能夠處理事務(wù)。 Spring很顯然不是事務(wù)性資源,但是它可...

    graf 評論0 收藏0

發(fā)表評論

0條評論

姘存按

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<