动态代理-cglib分析

[复制链接]
发表于 2023-2-8 12:00:14 | 显示全部楼层 |阅读模式
生成代理类文件的方式

jvm添加此启动参数,后面就是代理类class生成的地址
-Dcglib.debugLocation=~/baldhead/java/dynamic-proxy-cglib/src/main/java/com/baldhead/dynamic/proxy/cglib/class
添加这个参数之后,CGLIB就会把生成的代理Class文件存在指定的路径
生成动态代理对象流程


  • CGLIB首先生成代理类
  • 代码中的 static 静态代码块 会调用 CGLIB$STATICHOOK1(); 方法,方法作用
    3. 新建一个名字为 CGLIB$THREAD_CALLBACKS的ThreadLocal,用来存放所设置的 callback
    4. 使用反射找到代理类中的所有方法,包括(toString、hashCode、equals、clone),名字为模板 CGLIB$METHODNAME$数字编号$Method
    并且给对应的方法创建代理方法  名字模板CGLIB$METHODNAME$数字编号$Proxy
  • 调用构造方法创建代理对象
  • 然后CGLIB会调用代理对象的 CGLIB$SET_THREAD_CALLBACKS 方法,将传入的 callBack存到 ThreadLocal(CGLIB$THREAD_CALLBACKS) 中去
  • 后续在对象执行需要代理的方法的时候,就会从CGLIB$THREAD_CALLBACKS中拿到所设置的 CallBack并调用它的intercept()方法
代理对象的创建
  1. static void CGLIB$STATICHOOK1() {
  2.         CGLIB$THREAD_CALLBACKS = new ThreadLocal();
  3.         CGLIB$emptyArgs = new Object[0];
  4.         Class var0 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService$$EnhancerByCGLIB$$e34eec9a");
  5.         Class var1;
  6.         CGLIB$test$0$Method = ReflectUtils.findMethods(new String[]{"test", "()V"}, (var1 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService")).getDeclaredMethods())[0];
  7.         CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
  8.     }
复制代码
以上代码经过简化的,主要看下面给出的一行
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
对应的方法如下
  1. public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
  2.         /**
  3.          * 这几个参数都可以找到入参对象
  4.          * c1: 被代理类对象的class,也就是原始对象的class
  5.          * c2: 代理类对象的 class
  6.          * desc: 方法的返回值类型
  7.          * name1: 原始代理方法的名称
  8.          * name2: 代理方法在代理类中的名称(CGLIB$test$0)
  9.          */
  10.         MethodProxy proxy = new MethodProxy();
  11.         proxy.sig1 = new Signature(name1, desc);
  12.         proxy.sig2 = new Signature(name2, desc);
  13.         proxy.createInfo = new CreateInfo(c1, c2);
  14.         return proxy;
  15.     }
复制代码
在MethodProxy中有三个很重要的属性

  • sig1: 表示test方法
  • sig2: 表示 CGLIB$test$0 方法
  • createInfo: 表示原始类和代理类
invoke和invokeSuper方法
  1. public Object invoke(Object obj, Object[] args) throws Throwable {
  2.         try {
  3.             this.init();
  4.             FastClassInfo fci = this.fastClassInfo;
  5.             return fci.f1.invoke(fci.i1, obj, args);
  6.         } catch (InvocationTargetException var4) {
  7.             throw var4.getTargetException();
  8.         } catch (IllegalArgumentException var5) {
  9.             if (this.fastClassInfo.i1 < 0) {
  10.                 throw new IllegalArgumentException("Protected method: " + this.sig1);
  11.             } else {
  12.                 throw var5;
  13.             }
  14.         }
  15.     }
  16.     public Object invokeSuper(Object obj, Object[] args) throws Throwable {
  17.         try {
  18.             this.init();
  19.             FastClassInfo fci = this.fastClassInfo;
  20.             return fci.f2.invoke(fci.i2, obj, args);
  21.         } catch (InvocationTargetException var4) {
  22.             throw var4.getTargetException();
  23.         }
  24.     }
复制代码
两个方法大差不差的,但是都用到了一个对象  fastClassInfo 这个对象是在 init()方法中构造的
  1. private void init() {
  2.         if (this.fastClassInfo == null) {
  3.             synchronized(this.initLock) {
  4.                 if (this.fastClassInfo == null) {
  5.                     CreateInfo ci = this.createInfo;
  6.                     FastClassInfo fci = new FastClassInfo();
  7.                     fci.f1 = helper(ci, ci.c1);
  8.                     fci.f2 = helper(ci, ci.c2);
  9.                     fci.i1 = fci.f1.getIndex(this.sig1);
  10.                     fci.i2 = fci.f2.getIndex(this.sig2);
  11.                     this.fastClassInfo = fci;
  12.                     this.createInfo = null;
  13.                 }
  14.             }
  15.         }
  16.     }
复制代码
fastClassInfo对象中主要是有四个属性

  • f1: 原始类对应的一个FastClass 代理对象
  • f2: 代理类对应的一个FastClass 代理对象
  • i1: test方法在原始类对应的一个FastClass代理对象中的下标
  • i2: CGLIB$test$0方法在代理类对应的一个 FastClass 代理对象中的下标
    这里产生了两个代理对象,你说好巧不巧,正好产生的代理,class有3个,其中有两个继承 FastClass, 另外一个继承原始类并且实现 Factory接口

其实这两个类类似,都是针对某一个类的FastClass代理类,所以我们好好看一下UserService所对应的FastClass该类主要有:

  • 一个构造方法
  • public int getlndex(Signature var1)
  • public int getlndex(String var1, Classll var2)
  • public int getlndex(ClassI var1)
  • public Object invoke(int var1, Object ar2, Objectll var3)
  • public Object newlnstance(int var1, Objectll var2)
  • public int getMaxlndex0
顾名思义,FastClass的作用是提高方法的执行速度,按照正常的实现,当我们调用MethodProxy对象的invokel或invokeSuper0方法时,首先应该要做到的就是找到对应的Method对象,比如:

  • 执行invoke0,要找到test方法对应的Method对象
  • 执行invokeSuper0,要找到CGLIBstest$00方法对应的Method对象然后利用反射来执行Method。
那么FastClass的机制就是预先把UserService类或UserService代理类中的所有方法做一个索引,比如:
  1.   public int getIndex(Signature var1) {
  2.         String var10000 = var1.toString();
  3.         switch (var10000.hashCode()) {
  4.             case -2055565910:
  5.                 if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
  6.                     return 19;
  7.                 }
  8.                 break;
  9.             case -1659690448:
  10.                 if (var10000.equals("CGLIB$test$4()V")) {
  11.                     return 20;
  12.                 }
  13.                 break;
  14.             case -1457535688:
  15.                 if (var10000.equals("CGLIB$STATICHOOK1()V")) {
  16.                     return 12;
  17.                 }
  18.                 break;
  19.             case -1422510685:
  20.                 if (var10000.equals("test()V")) {
  21.                     return 7;
  22.                 }
  23.                 break;
  24.             case -1411872516:
  25.                 if (var10000.equals("CGLIB$hashCode$2()I")) {
  26.                     return 15;
  27.                 }
  28.                 break;
  29.         // 省略部分代码
  30.         }
  31.         return -1;
  32.     }
复制代码
一旦调用 getIndex(Signature var1) 方法,就对得到对应方法返回的索引,例如这里就是test方法返回的对应的索引就是7
再回到init 方法
  1. private void init() {
  2.         if (this.fastClassInfo == null) {
  3.             synchronized(this.initLock) {
  4.                 if (this.fastClassInfo == null) {
  5.                     CreateInfo ci = this.createInfo;
  6.                     FastClassInfo fci = new FastClassInfo();
  7.                     fci.f1 = helper(ci, ci.c1);
  8.                     fci.f2 = helper(ci, ci.c2);
  9.                     fci.i1 = fci.f1.getIndex(this.sig1);
  10.                     fci.i2 = fci.f2.getIndex(this.sig2);
  11.                     this.fastClassInfo = fci;
  12.                     this.createInfo = null;
  13.                 }
  14.             }
  15.         }
  16.     }
复制代码
init方法中的两个 helper方法就是去生成原始类和代理类的 FactClass代理类,后面个两个getIndex方法
1. 第一个fci.f1.getIndex(this.sig1)就是去获取原始类对应的FastClass代理类中 test方法的下标i1
2. 第二个 fci.f2.getIndex(this.sig2)就是去获取代理类对应的FastClass代理类中$test$0方法的下标i2
然后会把两个下标都记录在 fastClassInfo 对象中
后面就是我们看到的invoke和invokeSuper中调用的两个方法

  • invoke

    • fci.f1.invoke(fci.i1, obj, args);
      执行原始类对应的FastClass 代理类的invoke方法

  • invokeSuper

    • fci.f2.invoke(fci.i2, obj, args);
      执行代理类对应的FastClass代理类的invoke方法

例如: 原始类对应的FastClass 代码
  1. public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
  2.         UserService var10000 = (UserService)var2;
  3.         int var10001 = var1;
  4.         try {
  5.             switch (var10001) {
  6.                 case 0:
  7.                     var10000.test();
  8.                     return null;
  9.                 case 1:
  10.                     return new Boolean(var10000.equals(var3[0]));
  11.                 case 2:
  12.                     return var10000.toString();
  13.                 case 3:
  14.                     return new Integer(var10000.hashCode());
  15.             }
  16.         } catch (Throwable var4) {
  17.             throw new InvocationTargetException(var4);
  18.         }
  19.         throw new IllegalArgumentException("Cannot find matching method/constructor");
  20.     }
复制代码
这个代码比较简单,第一个参数就是执行方法的index,第二个参数就是原始类,第三个就是原始类的参数
如果传入的index 是0 ,那么就会去执行test方法
代理类对应的FastClass代理类的invoke方法也是类似
  1. public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
  2.         UserService..EnhancerByCGLIB..e34eec9a var10000 = (UserService..EnhancerByCGLIB..e34eec9a)var2;
  3.         int var10001 = var1;
  4.         try {
  5.             switch (var10001) {
  6.                 case 0:
  7.                     return new Boolean(var10000.equals(var3[0]));
  8.                 case 1:
  9.                     return var10000.toString();
  10.                 case 2:
  11.                     return new Integer(var10000.hashCode());
  12.                 case 3:
  13.                     return var10000.clone();
  14.                 case 4:
  15.                     return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
  16.                 case 5:
  17.                     return var10000.newInstance((Callback[])var3[0]);
  18.                 case 6:
  19.                     return var10000.newInstance((Callback)var3[0]);
  20.                 case 7:
  21.                     var10000.test();
  22.                     return null;
  23.                 case 8:
  24.                     e34eec9a.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
  25.                     return null;
  26.                 case 9:
  27.                     e34eec9a.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
  28.                     return null;
  29.                 case 10:
  30.                     var10000.setCallbacks((Callback[])var3[0]);
  31.                     return null;
  32.                 case 11:
  33.                     return var10000.getCallback(((Number)var3[0]).intValue());
  34.                 case 12:
  35.                     return var10000.getCallbacks();
  36.                 case 13:
  37.                     var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
  38.                     return null;
  39.                 case 14:
  40.                     return e34eec9a.CGLIB$findMethodProxy((Signature)var3[0]);
  41.                 case 15:
  42.                     e34eec9a.CGLIB$STATICHOOK1();
  43.                     return null;
  44.                 case 16:
  45.                     var10000.CGLIB$test$0();
  46.                     return null;
  47.                 case 17:
  48.                     return new Integer(var10000.CGLIB$hashCode$3());
  49.                 case 18:
  50.                     return new Boolean(var10000.CGLIB$equals$1(var3[0]));
  51.                 case 19:
  52.                     return var10000.CGLIB$toString$2();
  53.                 case 20:
  54.                     return var10000.CGLIB$clone$4();
  55.             }
  56.         } catch (Throwable var4) {
  57.             throw new InvocationTargetException(var4);
  58.         }
  59.         throw new IllegalArgumentException("Cannot find matching method/constructor");
  60.     }
复制代码
例如传入的index 是16 那么执行的就是 var10000.CGLIB$test$0();
如果传入的index是 7 那么执行的就是var10000.test();
var10000 是传入对象强转为UserService..EnhancerByCGLIB..e34eec9a类的对象,UserService..EnhancerByCGLIB..e34eec9a类其实就是UserService的代理类
invokeSuper结论

所以当我们执行invokeSuper方法的时候,不能传入原始类(UserService)只能传入代理类对象,不然就无法转换成为代理类类型
所以FastClass 快的地方就是预先把所有的方法信息都生成了对应的index,在真正的去执行的时候不用再去找Method对象,直接传入对应方法的index就可以直接执行对应的方法了

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表