国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

姘存按 / 2482人閱讀

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

現(xiàn)象描述

上周同事發(fā)現(xiàn)其基于mySql實(shí)現(xiàn)的分布式鎖的線上代碼存在問(wèn)題,代碼簡(jiǎ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();
    }
}

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

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

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

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

首先復(fù)習(xí)下相關(guān)知識(shí):Spring AOP、JDK動(dòng)態(tài)代理、CGLIB、AspectJ、@Aspect

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

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

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

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

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

進(jìn)一步分析

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

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

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

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

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

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

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

雖然CGLIB通過(guò)繼承的方式是可以支持public、protected、package級(jí)別的方法增強(qiáng)的,但是由于JDK動(dòng)態(tài)代理必須通過(guò)Java接口,只能支持public級(jí)別的方法,因此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注入的,因此實(shí)際上指向XService$$Cglib對(duì)象,xService.doInside()因此也能正確的指向增強(qiáng)后的方法。

一種錯(cuò)誤的解決辦法:改造為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();
    }
}

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

實(shí)際上,這還是避免不了自調(diào)用的問(wèn)題,因?yàn)檫@是動(dòng)態(tài)代理的普遍問(wèn)題,無(wú)論是JDK動(dòng)態(tài)代理還是CGLIB動(dòng)態(tài)代理。

總結(jié)

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

Reference

Spring AOP 實(shí)現(xiàn)原理與 CGLIB 應(yīng)用

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

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

Spring @Transactional原理及使用

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/67694.html

相關(guān)文章

  • 一次線上問(wèn)題排查解決過(guò)程

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

    宋華 評(píng)論0 收藏0
  • 一次線上問(wèn)題排查解決過(guò)程

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

    airborne007 評(píng)論0 收藏0
  • 一次線上問(wèn)題排查解決過(guò)程

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

    HollisChuang 評(píng)論0 收藏0
  • Java進(jìn)階之路

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

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

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

    graf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<