摘要:由于的限制,無(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也可以配合使用。
進(jìn)一步分析@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)代理是不兼容的。
既然事務(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í)行了父類XService的doOutside()方法,所以在執(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ū)。
“自調(diào)用”的解決方法 1. 最好在被代理類的外部調(diào)用其方法 2. 自注入(Self Injection, from Spring 4.3)雖然CGLIB通過(guò)繼承的方式是可以支持public、protected、package級(jí)別的方法增強(qiáng)的,但是由于JDK動(dòng)態(tài)代理必須通過(guò)Java接口,只能支持public級(jí)別的方法,因此Spring AOP不得不取消非public方法的支持。
@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)的方法。
ReferenceSpring 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
摘要:排查異常日志,發(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...
摘要:排查異常日志,發(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...
摘要:排查異常日志,發(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...
摘要:探索專為而設(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...
摘要:和事務(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ù)性資源,但是它可...
閱讀 2568·2023-04-25 17:33
閱讀 648·2021-11-23 09:51
閱讀 2951·2021-07-30 15:32
閱讀 1395·2019-08-29 18:40
閱讀 1940·2019-08-28 18:19
閱讀 1465·2019-08-26 13:48
閱讀 2237·2019-08-23 16:48
閱讀 2275·2019-08-23 15:56