激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Spring AOP注解失效的坑及JDK動態(tài)代理

Spring AOP注解失效的坑及JDK動態(tài)代理

2021-04-16 11:58白色夜空 JAVA教程

這篇文章主要介紹了Spring AOP注解失效的坑及JDK動態(tài)代理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

@Transactional @Async等注解不起作用

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

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

JDK動態(tài)代理

以一個簡單的例子理解一下JDK動態(tài)代理的基本原理:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//目標類接口
public interface JDKProxyTestService {
  void run();
}
 
//目標類
public class JDKProxyTestServiceImpl implements JDKProxyTestService {
  public void run(){
    System.out.println("do something...");
  }
}
 
//代理類
public class TestJDKProxy implements InvocationHandler {
 
  private Object targetObject; //代理目標對象
 
  //構造代理對象
  public Object newProxy(Object targetObject) {
    this.targetObject = targetObject;
    return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
        targetObject.getClass().getInterfaces(), this);
  }
 
  //利用反射,在原邏輯上進行邏輯增強
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    //模擬事務開始
    assumeBeginTransaction();
    //原執(zhí)行邏輯
    Object ret = method.invoke(targetObject, args);
    //模擬事務提交
    assumeCommitTransaction();
    return ret;
  }
 
  private void assumeBeginTransaction() {
    System.out.println("模擬事務開始...");
  }
 
  private void assumeCommitTransaction() {
    System.out.println("模擬事務提交...");
  }
}
 
//測試
public class Test {
  public static void main(String[] args) {
    TestJDKProxy jdkProxy = new TestJDKProxy();
    JDKProxyTestService proxy = (JDKProxyTestService) jdkProxy.newProxy(new JDKProxyTestServiceImpl());
    proxy.run();
  }
}

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

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

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

Spring AOP注解失效及解決

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

同一個類中,方法A調用方法B(方法B上加有注解),注解無效

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

如果你想要通過類X的對象直接調用其中帶注解的A方法,此注解是有效的。因為此時,Spring會判斷你將要調用的方法上存在AOP注解,那么會使用類X的代理對象調用A方法。

但是假設類X中的A方法會調用帶注解的B方法,而你依然想要通過類X對象調用A方法,那么B方法上的注解是無效的。因為此時Spring判斷你調用的A并無注解,所以使用的還是原對象而非代理對象。接下來A再調用B時,在原對象內B方法的注解當然無效了。

解決方法:

最簡單的方式當然是可以讓方法A和B沒有依賴,能夠直接通過類X的對象調用B方法。

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

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

?
1
2
3
4
5
// 在A方法內部調用B方法
// 1.直接調用B,注解失效。
B()
// 2.拿到代理類對象,再調用B。
((X)AopContext.currentProxy()).B()

AOP注解方法里使用@Autowired對象為null

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

最終發(fā)現(xiàn),導致這種情況的原因是因為方法為private。因為Spring不管使用的是JDK動態(tài)代理還是CGLIB動態(tài)代理,一個是針對實現(xiàn)接口的類,一個是通過子類實現(xiàn)。無論是接口還是父類,顯然都不能出現(xiàn)private方法,否則子類或實現(xiàn)類都不能覆蓋到。

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

所以如果方法需要使用AOP注解,請把它設置為非private方法。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://segmentfault.com/a/1190000014015664

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费观看9x视频网站在线观看 | 国产合集91合集久久日 | 操穴视频 | 亚洲午夜免费电影 | 一级黄色免费观看 | 特级a欧美做爰片毛片 | 中文字幕xxx| 中文字幕欧美日韩 | 久久福利电影网 | 日韩视频一二区 | 1级黄色毛片 | 免费观看一级淫片 | 国产精品呻吟 | 黄色试看视频 | 欧美一级高潮 | 亚洲激情91 | 精品一区免费 | 激情宗合网 | 青青草免费观看 | 国产在线观看 | 中文字幕电影免费播放 | 高潮激情aaaaa免费看 | 噜噜色av | 欧美一级黄色免费看 | 亚洲国产二区 | 午夜a狂野欧美一区二区 | 91一区二区三区久久久久国产乱 | 精品成人久久久 | 国产精品久久久久久久久久久久久久久久 | 黄色网络免费看 | 国产欧美日韩视频在线观看 | 久久精品99久久久久久2456 | 成人短视频在线播放 | 欧美一级片网站 | 久久久久久久久国产精品 | 成人毛片一区 | 日本特级a一片免费观看 | 久久国产精品99久久人人澡 | 国产1区2区在线 | 黄视频网址| 久久丝袜脚交足黄网站免费 |