日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring AOP注解失效原因

發(fā)布時(shí)間:2023/12/20 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring AOP注解失效原因 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

@Transactional @Async等注解不起作用

JDK動(dòng)態(tài)代理

Spring AOP注解失效原因及解決

原因1:同一個(gè)類中,方法A調(diào)用方法B(方法B上加有注解),注解無效

原因2:AOP注解方法里使用@Autowired對(duì)象為null


@Transactional @Async等注解不起作用

之前很多人在使用Spring中的@Transactional, @Async等注解時(shí),都多少碰到過注解不起作用的情況。

為什么會(huì)出現(xiàn)這些情況呢?因?yàn)檫@些注解的功能實(shí)際上都是Spring AOP實(shí)現(xiàn)的,而其實(shí)現(xiàn)原理是通過代理實(shí)現(xiàn)的。

JDK動(dòng)態(tài)代理

以一個(gè)簡(jiǎn)單的例子理解一下JDK動(dòng)態(tài)代理的基本原理:

//目標(biāo)類接口 public interface JDKProxyTestService {void run(); }//目標(biāo)類 public class JDKProxyTestServiceImpl implements JDKProxyTestService {public void run(){System.out.println("do something...");} }//代理類 public class TestJDKProxy implements InvocationHandler {private Object targetObject; //代理目標(biāo)對(duì)象//構(gòu)造代理對(duì)象public Object newProxy(Object targetObject) {this.targetObject = targetObject;return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(), this);}//利用反射,在原邏輯上進(jìn)行邏輯增強(qiáng)public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//模擬事務(wù)開始assumeBeginTransaction();//原執(zhí)行邏輯Object ret = method.invoke(targetObject, args);//模擬事務(wù)提交assumeCommitTransaction();return ret;}private void assumeBeginTransaction() {System.out.println("模擬事務(wù)開始...");}private void assumeCommitTransaction() {System.out.println("模擬事務(wù)提交...");} }//測(cè)試 public class Test { public static void main(String[] args) {TestJDKProxy jdkProxy = new TestJDKProxy();JDKProxyTestService proxy = (JDKProxyTestService) jdkProxy.newProxy(new JDKProxyTestServiceImpl());proxy.run();} }

上面的例子應(yīng)該能夠清楚的解釋JDK動(dòng)態(tài)代理的原理了。它利用反射機(jī)制,生成了一個(gè)實(shí)現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。我們通過代理類對(duì)象調(diào)用方法時(shí),實(shí)際上會(huì)先調(diào)用其invoke方法,里面再調(diào)用原方法。這樣我們可以在原方法邏輯的前后統(tǒng)一添加處理邏輯。

Spring還有一種動(dòng)態(tài)代理方式是CGLIB動(dòng)態(tài)代理。它是把代理對(duì)象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理。雖然處理方式不一樣,但是代理的思想都是一致的。

如果被代理的目標(biāo)對(duì)象實(shí)現(xiàn)了接口,那么Spring會(huì)默認(rèn)使用JDK動(dòng)態(tài)代理。所有該目標(biāo)類型實(shí)現(xiàn)的接口都將被代理。若該目標(biāo)對(duì)象沒有實(shí)現(xiàn)任何接口,則創(chuàng)建一個(gè)CGLIB代理。

Spring AOP注解失效原因及解決

基于以上對(duì)于動(dòng)態(tài)代理原理的分析,我們來看以下兩個(gè)常見的問題:

原因1:同一個(gè)類中,方法A調(diào)用方法B(方法B上加有注解),注解無效

針對(duì)所有的Spring AOP注解,Spring在掃描bean的時(shí)候如果發(fā)現(xiàn)有此類注解,那么會(huì)動(dòng)態(tài)構(gòu)造一個(gè)代理對(duì)象。

如果你想要通過類X的對(duì)象直接調(diào)用其中帶注解的A方法,此注解是有效的。因?yàn)榇藭r(shí),Spring會(huì)判斷你將要調(diào)用的方法上存在AOP注解,那么會(huì)使用類X的代理對(duì)象調(diào)用A方法。

但是假設(shè)類X中的A方法會(huì)調(diào)用帶注解的B方法,而你依然想要通過類X對(duì)象調(diào)用A方法,那么B方法上的注解是無效的。因?yàn)?strong>此時(shí)Spring判斷你調(diào)用的A并無注解,所以使用的還是原對(duì)象而非代理對(duì)象。接下來A再調(diào)用B時(shí),在原對(duì)象內(nèi)B方法的注解當(dāng)然無效了。

解決方法:

最簡(jiǎn)單的方式當(dāng)然是可以讓方法A和B沒有依賴,能夠直接通過類X的對(duì)象調(diào)用B方法。

但是很多時(shí)候可能我們的邏輯拆成這樣寫并不好,那么就還有一種方法:想辦法手動(dòng)拿到代理對(duì)象。

AopContext類有一個(gè)currentProxy()方法,能夠直接拿到當(dāng)前類的代理對(duì)象。那么以上的例子,就可以這樣解決:

// 在A方法內(nèi)部調(diào)用B方法 // 1.直接調(diào)用B,注解失效。 B() // 2.拿到代理類對(duì)象,再調(diào)用B。 ((X)AopContext.currentProxy()).B()

原因2:AOP注解方法里使用@Autowired對(duì)象為null

在之前的使用中,出現(xiàn)過在加上注解的方法中,使用其他注入的對(duì)象時(shí),發(fā)現(xiàn)對(duì)象并沒有被注入進(jìn)來,為null。

最終發(fā)現(xiàn),導(dǎo)致這種情況的原因是因?yàn)榉椒閜rivate。因?yàn)镾pring不管使用的是JDK動(dòng)態(tài)代理還是CGLIB動(dòng)態(tài)代理,一個(gè)是針對(duì)實(shí)現(xiàn)接口的類,一個(gè)是通過子類實(shí)現(xiàn)。無論是接口還是父類,顯然都不能出現(xiàn)private方法,否則子類或?qū)崿F(xiàn)類都不能覆蓋到。

如果方法為private,那么在代理過程中,根本找不到這個(gè)方法,引起代理對(duì)象創(chuàng)建出現(xiàn)問題,也導(dǎo)致了有的對(duì)象沒有注入進(jìn)去。

所以如果方法需要使用AOP注解,請(qǐng)把它設(shè)置為非private方法。

總結(jié)

以上是生活随笔為你收集整理的spring AOP注解失效原因的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。