<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      cglib FastClass機制

      前言

      關于動態代理的一些知識,以及cglib與jdk動態代理的區別,在這一篇已經介紹過,不熟悉的可以先看下。
      本篇我們來學習一下cglib的FastClass機制,這是cglib與jdk動態代理的一個主要區別,也是一個面試考點。
      我們知道jdk動態代理是使用InvocationHandler接口,在invoke方法內,可以使用Method方法對象進行反射調用,反射的一個最大問題是性能較低,cglib就是通過使用FastClass來優化反射調用,提升性能,接下來我們就看下它是如何實現的。

      示例

      我們先寫一個hello world,讓代碼跑起來。如下:

      public class HelloWorld {
      
      	public void print() {
      		System.out.println("hello world");
      	}
      }
      
      public class HelloWorldInterceptor implements MethodInterceptor {
      	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      		System.out.println("before hello world");
      		methodProxy.invokeSuper(o, objects);
      		System.out.println("after hello world");
      		return null;
      	}
      }
      

      非常簡單,就是使用MethodInterceptor在HelloWorld類print方法前后打印一句話,模擬對一個方法前后織入自定義邏輯。
      接著使用cglib Enhancer類,創建動態代理對象,設置MethodInterceptor,調用方法。
      為了方便觀察源碼,我們將cglib生成的動態代理類保存下來。

      
      //將生成的動態代理類保存下來
      System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");
      
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(HelloWorld.class);
      enhancer.setCallback(new HelloWorldInterceptor());
      
      HelloWorld target = (HelloWorld) enhancer.create();
      target.print();
      

      輸出

      before hello world
      hello world
      after hello world
      

      FastClass機制

      我們知道cglib是通過繼承實現的,動態代理類會繼承被代理類,并重寫它的方法,所以它不需要像jdk動態代理一樣要求被代理對象有實現接口,因此比較靈活。
      既然是通過繼承實現的,那應該生成一個類就可以了,但是通過上面的路徑觀察,可以看到生成了3個文件,其中兩個帶有FastClass關鍵字。
      這三個類分別是:動態代理類,動態代理類的FastClass,被代理對象的FastClass,從名稱上也可以看出它們的關系。

      其中動態代理類繼承了被代理類,并重寫了父類的所有方法,包括父類的父類的方法,包括Object類的equals方法和toString方法等。

      public class HelloWorld$$EnhancerByCGLIB$$49f9f9c8 extends HelloWorld implements Factory {
      }
      

      這里我們只關注print方法,如下:

      第一個直接調用父類方法,也就是被代理對象的方法;第二個會先判斷有沒有攔截器,如果沒有也是直接調用父類方法,否則調用MethodInterceptor的intercept方法,對于我們這里就是HelloWorldInterceptor。
      看下intercept的幾個參數分別是什么,這幾個參數的初始化在動態代理類的靜態代碼塊中都可以找到。
      第1個表示動態代理對象。
      第2個是被代理對象方法的Method,就是HelloWorld.print。
      第3個表示方法參數。
      第4個是MethodProxy對象,通過名字我們可以知道它是方法的代理,每一個方法都會有一個對應的MethodProxy,它包含被代理對象、代理對象、以及對應的方法元信息。

      這里我們重點關注MethodProxy,它的初始化如下:

      CGLIB$print$0$Proxy = MethodProxy.create(var1, var0, "()V", "print", "CGLIB$print$0");       
      

      第1個參數表示被代理對象的Class。
      第2個參數表示動態代理對象的Class。
      第3個參數是方法的返回值。
      第4個參數表示被代理對象的方法名稱。
      第5個參數表示對應動態代理對象的方法名稱。

      MethodProxy對象創建好后,我們上面就是通過它進行調用的

      methodProxy.invokeSuper(o, objects);
      

      invokeSuper主要源碼如下:

      public Object invokeSuper(Object obj, Object[] args) throws Throwable {
          init();
          FastClassInfo fci = fastClassInfo;
          return fci.f2.invoke(fci.i2, obj, args);
      }
      
      private void init()
      {
          if (fastClassInfo == null)
          {
              synchronized (initLock)
              {
                  if (fastClassInfo == null)
                  {
                      CreateInfo ci = createInfo;
      
                      FastClassInfo fci = new FastClassInfo();
                      fci.f1 = helper(ci, ci.c1); //被代理對象的FastClass
                      fci.f2 = helper(ci, ci.c2); //動態代理對象的FastClass
                      fci.i1 = fci.f1.getIndex(sig1); //被代理對象方法的索引下標
                      fci.i2 = fci.f2.getIndex(sig2); //動態代理對象方法的索引下標,這里是:CGLIB$print$0 
                      fastClassInfo = fci;
                      createInfo = null;
                  }
              }
          }
      }
      

      init方法使用加鎖+雙檢查的方式,只會初始化一次fastClassInfo變量,它用volatile關鍵字進行修飾,這里涉及到java字節碼重排問題,具體可以參考我們之前的分析:happend before原則

      接著回到invokeSuper方法,fci.f2.invoke(fci.i2, obj, args); 實際就是調用動態代理對象的FastClass的invoke方法,并把要調用方法的索引下標i2傳過去。
      至于方法的索引下標是怎么找到的,可以看動態代理對象的FastClass的getIndex方法,其實就是通過方法的名稱、參數個數、參數類型,完全匹配,點到源碼文件可以看到有大量的switch分支判斷。
      這里我們可以看到print方法的索引下標就是18。

      public int getIndex(String var1, Class[] var2) {
          switch (var1.hashCode()) {
              case -1295482945:
                  if (var1.equals("equals")) {
                      switch (var2.length) {
                          case 1:
                              if (var2[0].getName().equals("java.lang.Object")) {
                                  return 0;
                              }
                      }
                  }
              break;
              case 770871766:
                  if (var1.equals("CGLIB$print$0")) {
                      switch (var2.length) {
                          case 0:
                              return 18;
                      }
                  }
              break;
          }
      }
      
       public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
          HelloWorld..EnhancerByCGLIB..49f9f9c8 var10000 = (HelloWorld..EnhancerByCGLIB..49f9f9c8)var2;
          int var10001 = var1;
      
          //...
          switch (var10001) {                
              //...
              case 18:
                  var10000.CGLIB$print$0();
                  return null;
          }
       }    
      

      可以看到最終調用到動態代理類的CGLIB$print$0方法,也就是:

          final void CGLIB$print$0() {
              super.print();
          }
      

      最終調用的就是父類的方法。我們畫張圖總結一下,有興趣的同學跟著圖和代碼邏輯應該可以快速理解。

      總結

      經過上面的分析,我們可以看到cglib在整個調用過程并沒有用到反射,而是使用FastClass對每個方法進行索引,通過方法名稱,參數長度,參數類型就可以找到具體的方法,因此性能較好。但也有缺點,首次調用需要生成3個類,會比較慢。在我們實際開發中,特別是一些框架開發,如果有類似的場景也可以借助FastClass對反射進行優化,如:

      MyClass cs = new MyCase();
      FastClass fastClass = FastClass.create(Case.class);
      int index = fastClass.getIndex("test", new Class[]{Integer.class});
      Object invoke = fastClass.invoke(index, cs, new Object[1]);
      

      另外MethodProxy還有一個invoke方法,如果我們換一下調用這個方法會發生?留給大家自己嘗試。

      methodProxy.invokeSuper(o, objects);
      //換成 methodProxy.invoke(o, objects);
      

      更多分享,歡迎關注我的github:https://github.com/jmilktea/jtea

      posted @ 2024-03-12 10:21  jtea  閱讀(402)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 黄色亚洲一区二区三区四区| 性XXXX视频播放免费直播| 人妻无码中文专区久久app| 伦伦影院精品一区| 中文字幕日韩精品有码视频| 图片区小说区av区| 午夜男女爽爽影院在线| 国产精品看高国产精品不卡| 乱女乱妇熟女熟妇综合网| 免费 黄 色 人成 视频 在 线| 一区二区三区无码视频免费福利| 精品自拍偷拍一区二区三区| 午夜在线观看成人av| 国产v综合v亚洲欧美久久| 无码加勒比一区二区三区四区| 视频一区视频二区制服丝袜| 3d动漫精品一区二区三区| 天天躁日日躁狠狠躁一区| 中文字幕亚洲人妻系列| 大埔区| 性做久久久久久久久| 不卡高清AV手机在线观看| 苍山县| 一区二区三区四区高清自拍| 好男人官网资源在线观看| 性一交一乱一乱一视频| 九九九国产精品成人免费视频| 亚洲人成网站77777在线观看| 台中市| 伊人精品成人久久综合97| 国产精品久久久久不卡绿巨人| 欧美成人h精品网站| 亚洲成片在线看一区二区| 亚洲AV日韩AV激情亚洲 | 国色天香中文字幕在线视频| 久久精品国产熟女亚洲av| 色噜噜亚洲精品中文字幕| 日韩在线不卡免费视频一区| 亚洲av日韩av综合在线观看| 久久久久无码精品亚洲日韩| 国产一区二区不卡在线视频|