首页 > 其他分享 >动态代理-cglib分析

动态代理-cglib分析

时间:2023-02-08 11:46:50浏览次数:43  
标签:case return CGLIB var10000 代理 cglib fci 动态

生成代理类文件的方式

jvm添加此启动参数,后面就是代理类class生成的地址
-Dcglib.debugLocation=~/baldhead/java/dynamic-proxy-cglib/src/main/java/com/baldhead/dynamic/proxy/cglib/class

添加这个参数之后,CGLIB就会把生成的代理Class文件存在指定的路径

生成动态代理对象流程

  1. CGLIB首先生成代理类
  2. 代码中的 static 静态代码块 会调用 CGLIB$STATICHOOK1(); 方法,方法作用
    3. 新建一个名字为 CGLIB$THREAD_CALLBACKSThreadLocal,用来存放所设置的 callback
    4. 使用反射找到代理类中的所有方法,包括(toStringhashCodeequalsclone),名字为模板 CGLIB$METHODNAME$数字编号$Method
    并且给对应的方法创建代理方法 名字模板CGLIB$METHODNAME$数字编号$Proxy
  3. 调用构造方法创建代理对象
  4. 然后CGLIB会调用代理对象的 CGLIB$SET_THREAD_CALLBACKS 方法,将传入的 callBack存到 ThreadLocal(CGLIB$THREAD_CALLBACKS) 中去
  5. 后续在对象执行需要代理的方法的时候,就会从CGLIB$THREAD_CALLBACKS中拿到所设置的 CallBack并调用它的intercept()方法

代理对象的创建

static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService$$EnhancerByCGLIB$$e34eec9a");
        Class var1;
        CGLIB$test$0$Method = ReflectUtils.findMethods(new String[]{"test", "()V"}, (var1 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService")).getDeclaredMethods())[0];
        CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
    }

以上代码经过简化的,主要看下面给出的一行
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
对应的方法如下

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        /**
         * 这几个参数都可以找到入参对象
         * c1: 被代理类对象的class,也就是原始对象的class
         * c2: 代理类对象的 class
         * desc: 方法的返回值类型
         * name1: 原始代理方法的名称
         * name2: 代理方法在代理类中的名称(CGLIB$test$0)
         */
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

在MethodProxy中有三个很重要的属性

  • sig1: 表示test方法
  • sig2: 表示 CGLIB$test$0 方法
  • createInfo: 表示原始类和代理类

invoke和invokeSuper方法

 public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if (this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

两个方法大差不差的,但是都用到了一个对象 fastClassInfo 这个对象是在 init()方法中构造的

private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

fastClassInfo对象中主要是有四个属性

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

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

  1. 一个构造方法
  2. public int getlndex(Signature var1)
  3. public int getlndex(String var1, Classll var2)
  4. public int getlndex(ClassI var1)
  5. public Object invoke(int var1, Object ar2, Objectll var3)
  6. public Object newlnstance(int var1, Objectll var2)
  7. public int getMaxlndex0

顾名思义,FastClass的作用是提高方法的执行速度,按照正常的实现,当我们调用MethodProxy对象的invokel或invokeSuper0方法时,首先应该要做到的就是找到对应的Method对象,比如:

  1. 执行invoke0,要找到test方法对应的Method对象

  2. 执行invokeSuper0,要找到CGLIBstest$00方法对应的Method对象然后利用反射来执行Method。

那么FastClass的机制就是预先把UserService类或UserService代理类中的所有方法做一个索引,比如:

  public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch (var10000.hashCode()) {
            case -2055565910:
                if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                    return 19;
                }
                break;
            case -1659690448:
                if (var10000.equals("CGLIB$test$4()V")) {
                    return 20;
                }
                break;
            case -1457535688:
                if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                    return 12;
                }
                break;
            case -1422510685:
                if (var10000.equals("test()V")) {
                    return 7;
                }
                break;
            case -1411872516:
                if (var10000.equals("CGLIB$hashCode$2()I")) {
                    return 15;
                }
                break;
        // 省略部分代码
        }

        return -1;
    }

一旦调用 getIndex(Signature var1) 方法,就对得到对应方法返回的索引,例如这里就是test方法返回的对应的索引就是7
再回到init 方法

private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

init方法中的两个 helper方法就是去生成原始类和代理类的 FactClass代理类,后面个两个getIndex方法
1. 第一个fci.f1.getIndex(this.sig1)就是去获取原始类对应的FastClass代理类中 test方法的下标i1
2. 第二个 fci.f2.getIndex(this.sig2)就是去获取代理类对应的FastClass代理类中$test$0方法的下标i2

然后会把两个下标都记录在 fastClassInfo 对象中

后面就是我们看到的invokeinvokeSuper中调用的两个方法

  • invoke

    • fci.f1.invoke(fci.i1, obj, args);

      执行原始类对应的FastClass 代理类的invoke方法

  • invokeSuper

    • fci.f2.invoke(fci.i2, obj, args);

      执行代理类对应的FastClass代理类的invoke方法

例如: 原始类对应的FastClass 代码

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserService var10000 = (UserService)var2;
        int var10001 = var1;

        try {
            switch (var10001) {
                case 0:
                    var10000.test();
                    return null;
                case 1:
                    return new Boolean(var10000.equals(var3[0]));
                case 2:
                    return var10000.toString();
                case 3:
                    return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

这个代码比较简单,第一个参数就是执行方法的index,第二个参数就是原始类,第三个就是原始类的参数

如果传入的index 是0 ,那么就会去执行test方法

代理类对应的FastClass代理类的invoke方法也是类似

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserService..EnhancerByCGLIB..e34eec9a var10000 = (UserService..EnhancerByCGLIB..e34eec9a)var2;
        int var10001 = var1;

        try {
            switch (var10001) {
                case 0:
                    return new Boolean(var10000.equals(var3[0]));
                case 1:
                    return var10000.toString();
                case 2:
                    return new Integer(var10000.hashCode());
                case 3:
                    return var10000.clone();
                case 4:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 5:
                    return var10000.newInstance((Callback[])var3[0]);
                case 6:
                    return var10000.newInstance((Callback)var3[0]);
                case 7:
                    var10000.test();
                    return null;
                case 8:
                    e34eec9a.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 9:
                    e34eec9a.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 10:
                    var10000.setCallbacks((Callback[])var3[0]);
                    return null;
                case 11:
                    return var10000.getCallback(((Number)var3[0]).intValue());
                case 12:
                    return var10000.getCallbacks();
                case 13:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 14:
                    return e34eec9a.CGLIB$findMethodProxy((Signature)var3[0]);
                case 15:
                    e34eec9a.CGLIB$STATICHOOK1();
                    return null;
                case 16:
                    var10000.CGLIB$test$0();
                    return null;
                case 17:
                    return new Integer(var10000.CGLIB$hashCode$3());
                case 18:
                    return new Boolean(var10000.CGLIB$equals$1(var3[0]));
                case 19:
                    return var10000.CGLIB$toString$2();
                case 20:
                    return var10000.CGLIB$clone$4();
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

例如传入的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就可以直接执行对应的方法了

标签:case,return,CGLIB,var10000,代理,cglib,fci,动态
From: https://www.cnblogs.com/strict/p/17101086.html

相关文章

  • 终端(terminal)代理配置方案
    因为macos的终端在默认情况中,不走代理,只会走本地流量。即使我们使用了系统代理,也无法自动代理到公司内网。但是在利用终端安装一些软件配置一些服务的时候,我们不得不依赖......
  • SpringBoot动态生成接口
    原文链接:https://blog.csdn.net/lmchhh/article/details/128634606文章目录SpringBoot动态生成接口一,简单例子二,各种请求方法以及条件2.1无参GET方法2.2带1参的......
  • navMenus 的动态路由 嵌套 2
    <template><el-colid="navMenu"><el-menu:default-active="defaultActive"background-color="#0b172e"text-color="#A7B1C2"active-text-c......
  • navMenus 的动态路由 嵌套 1
    <template> <divclass="navMenu">  <templatev-for="menuinnavMenus">   <el-submenu    :key="menu.path"    :index="menu.path"......
  • UI画框选人加动态烘焙导航移动
     usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.AI;publicclassInputMag:MonoBehaviour{privateboolis......
  • 【Windows】使用代理后Microsoft Store等UWP应用无法正常运行解决方案(UWP Loopback)
    ✨使用代理后UWP应用无法正常运行使用ClashforWindows后UWP应用无法正常运行✨UWPLoopback使用ClashforWindows中的UWPLoopback勾选需要通过Loopback的UWP应......
  • 动态代理
    前言:本小节你将收获:了解代理模式、学会如何实现动态代理、深入探究动态代理的实现原理@Author:Akai-yuan@更新时间:2023/2/7代理模式介绍代理模式是一种设计模式,提供......
  • 静态库和动态库的区别
    程序编译成可执行程序的过程动态库会在链接过程中将自己名字信息打包到可执行文件中,但是不会把自己代码打包过去静态库则是会将自己的代码直接打包到可执行文件中静态库......
  • 动态库的制作和使用
    动态库的制作动态库也称为共享库注意一定加上-fpic动态库加载失败原因:程序启动之后,程序会把动态库的的内容加载到内存之中,通过ldd命令检查动态库依赖关系解决动......
  • 动态链接库的动态加载
    #include<stdio.h>#include<dlfcn.h>intmain(void){//使用动态加载的编译时必须链接dl库(-ldl),因为动态加载的各种方法都在dl库中//gccmain.c-otes......