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

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

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

      【深度思考】聊聊CGLIB動態代理原理

      2023-04-21 13:19  申城異鄉人  閱讀(3946)  評論(4)    收藏  舉報

      1. 簡介

      CGLIB的全稱是:Code Generation Library。

      CGLIB是一個強大的、高性能、高質量的代碼生成類庫,它可以在運行期擴展Java類與實現Java接口,

      底層使用的是字節碼處理框架ASM。

      Github地址:https://github.com/cglib/cglib

      CGLIB的Maven坐標如下所示:

      <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>3.3.0</version>
      </dependency>
      

      2. 示例

      首先,新增一個類:

      public class Coder {
          public void work() {
              System.out.println("認真寫bug……");
          }
      }
      

      然后,自定義一個方法攔截器,實現net.sf.cglib.proxy.MethodInterceptor接口并重寫intercept方法:

      public class AttendanceMethodInterceptor implements MethodInterceptor {
          @Override
          public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
              System.out.println("上班打卡……");
      
              Object result = proxy.invokeSuper(obj, args);
      
              System.out.println("下班打卡……");
      
              return result;
          }
      }
      

      重點看下Object result = proxy.invokeSuper(obj, args);,該行代碼最終會執行真正的目標方法,在這前后,我們可以添加一些增強邏輯。

      然后,新建個測試類,看下CGLIB動態代理如何使用:

      public class CglibProxyTest {
          public static void main(String[] args) {
              Enhancer enhancer = new Enhancer();
              enhancer.setSuperclass(Coder.class);
              enhancer.setCallback(new AttendanceMethodInterceptor());
              // 創建代理對象
              Object object = enhancer.create();
      
              Coder coder = (Coder) object;
              coder.work();
          }
      }
      

      運行以上代碼,效果如下圖所示:

      從運行結果可以看出,在目標方法的前后,執行了自定義的操作。

      3. 原理

      看下上面的測試類代碼,首先是創建了一個net.sf.cglib.proxy.Enhancer對象,然后調用了setSuperclass()方法

      將enhancer對象的父類設置為Coder類:

      緊接著調用了setCallback()方法將enhancer對象的方法攔截器設置為自定義的AttendanceMethodInterceptor:

      然后是調用enhancer對象的create()方法來生成一個代理對象。

      先打印下,簡單看下這個代理類的信息:

      圖中的com.zwwhnly.mybatisplusdemo.cglibproxy.Coder$$EnhancerByCGLIB$$8e91f654就是CGLIB生成的代理類的名稱。

      那么這個代理類具體是什么樣子呢?

      在上面的測試類代碼中(Object object = enhancer.create();代碼之前)添加以下一行代碼:

      System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib");
      

      然后再次運行,會看到項目根目錄下生成了一個cglib文件夾,自動生成的代理類就包含在其中:

      可以看到一共生成了5個類,這里重點關注下紅色標記的3個類。

      先看下Coder$$EnhancerByCGLIB$$8e91f654.class,這個類就是自動生成的代理類:

      可以看出Coder$$EnhancerByCGLIB$$8e91f654.class繼承了Coder類(也就是說自動生成的代理類其實是被代理類的一個子類),

      并且重寫了Coder類的work()方法,重寫后的work()方法會調用自定義的方法攔截器AttendanceMethodInterceptor里的intercept()

      方法。

      然后看下Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa,從名稱上可以看出這個類的前半段和上面的類的名稱

      是一樣的,后半段拼接上了$$FastClassByCGLIB$$4e5eb5aa,從功能上說,這個類是上面的代理類的索引類,重點關注下里面的

      getIndex()方法和invoke()方法:

      最后看下Coder$$FastClassByCGLIB$$398819d0,這個類是被代理類Coder的索引類,重點也是關注下里面的

      getIndex()方法和invoke()方法:

      知道了這3個類的作用后,再一步一步看下示例代碼中coder.work();的調用過程,因為coder是生成的代理類的實例,所以

      coder.work();首先調用的是Coder$$EnhancerByCGLIB$$8e91f654的work()方法:

      這里的var10000是自定義的方法攔截器AttendanceMethodInterceptor,所以執行的是紅色截圖里的intercept()方法,也就是:

      然后看下invokeSuper()方法:

      首先執行的是init()方法,在該方法內部對fastClassInfo字段進行了賦值:

      從上圖可以看出,fci.f1是自動生成的Coder類的索引類Coder$$FastClassByCGLIB$$398819d0,所以fci.i1 = fci.f1.getIndex(sig1);

      其實執行的是的Coder$$FastClassByCGLIB$$398819d0getIndex()方法:

      fci.f2是自動生成的代理類的索引類Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa

      所以fci.i2 = fci.f2.getIndex(sig2);其實執行的是的Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa

      getIndex()方法:

      看完init()方法后再回到invokeSuper()方法:

      上圖中的FastClassInfo fci = fastClassInfo;使用到的字段fastClassInfo在init()方法內部已經賦過值,

      fci.f2其實是自動生成的代理類的索引類Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa

      fci.i2值是1,

      所以fci.f2.invoke(fci.i2, obj, args);實際執行的是:

      這里的var10000其實是自動生成的代理類Coder$$EnhancerByCGLIB$$8e91f654的實例,所以接著調用的是

      代理類Coder$$EnhancerByCGLIB$$8e91f654CGLIB$work$0()方法:

      這里的super指的是Coder類,所以super.work();實際執行的是Coder類的work()方法:

      綜上所述,coder.work();的調用順序依次是:

      代理類--->自定義方法攔截器--->代理類索引類getIndex()方法-->代理類索引類invoke()方法--->代理類--->被代理類。

      4. JDK動態代理與CGLIB動態代理區別(面試常問)

      關于JDK動態代理,可以查看上一篇博客:【深度思考】聊聊JDK動態代理原理

      了解了JDK動態代理和CGLIB動態代理的原理后,現在來比較下兩者的區別,這也是面試時幾乎必問的一道面試題。

      1. 使用JDK動態代理,被代理類必須要實現接口,使用CGLIB動態代理,被代理類可以不實現接口

        原因分析:

        JDK動態代理生成的代理類繼承了java.lang.reflect.Proxy,因為Java是單繼承的,如果不通過實現接口的形式,

        無法對類進行擴展。

        CGLIB動態代理生成的代理類實際上是被代理類的子類,所以被代理類可以不實現接口。

      2. 自動生成類的數量不同

        JDK動態代理只會生成1個代理類,一般情況下名稱為:com.sun.proxy.$Proxy0

        CGLIB動態代理會生成好幾個類,核心的3個分別是:

        1)代理類:被代理類的子類,名稱格式為Coder$$EnhancerByCGLIB$$8e91f654,包名和被代理類包名一致。

        2)代理類的索引類:名稱格式為Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa

        包名和被代理類包名一致。

        3)被代理類的索引類:名稱格式為Coder$$FastClassByCGLIB$$398819d0,包名和被代理類包名一致。

      3. 生成代理類技術不同

        JDK動態代理使用JDK自帶的ProxyGenerator類生成字節碼文件。

        CGLIB動態代理使用ASM框架生成字節碼文件。

      4. 調用方式不同

        JDK動態代理:代理類--->InvocationHandler.invoke()--->被代理類方法(用到了反射)。

        CGLIB動態代理:代理類--->MethodInterceptor.intercept()--->代理類索引類getIndex()--->

        代理類索引類invoke()--->代理類--->被代理類。(直接調用)

      文章持續更新,歡迎關注微信公眾號「申城異鄉人」第一時間閱讀!

      主站蜘蛛池模板: 亚洲国产成人精品av区按摩| 91偷自国产一区二区三区| 成人精品自拍视频免费看| 蜜桃伦理一区二区三区| 麟游县| 五月婷婷久久中文字幕| 国产老熟女一区二区三区| 把腿张开ji巴cao死你h| 日本一区二区三区专线| 亚洲最大色综合成人av| 国产亚洲第一精品| 亚洲成A人片在线观看无码不卡 | 综合偷自拍亚洲乱中文字幕| 中文国产日韩欧美二视频| 中文字幕精品亚洲人成在线| 精品超清无码视频在线观看| 免费黄色大全一区二区三区| 韩国三级在线 中文字幕 无码| 人妻系列无码专区69影院| 日韩精品国产二区三区| 亚洲中文字幕无码一区无广告| 五月婷婷久久草| 亚洲一区二区三区人妻天堂| 国产成人亚洲欧美二区综合| 新乡县| jlzz大jlzz大全免费| 午夜性爽视频男人的天堂| 无码日韩精品一区二区三区免费| 国产亚洲精品综合99久久| 又黄又无遮挡AAAAA毛片| 日韩激情一区二区三区| 成人精品一区日本无码网| 乱人伦中文字幕成人网站在线| 女人18片毛片60分钟| 日韩欧激情一区二区三区| 亚洲欧美日韩成人综合一区| 久久九九精品99国产精品| 伊人久久大香线蕉综合网| 国产91久久精品一区二区| 国产综合久久99久久| 性色在线视频精品|