• CGLIB源码易懂解析


    1.测试代码

    public class InfoDemo {
        public void welcome (String person){
            System.out.println("welcome :" + person);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class CglibInfoProxy implements MethodInterceptor {
        private Object target;
    
        public Object newInstance(Object source) {
            target = source;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before method!!!");
            //生成的代理类字节码中调用
            Object value = methodProxy.invokeSuper(o, objects);
            //Object value = methodProxy.invoke(o, objects);
            return value;
        }
    
        public static void main(String[] args) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\development\\classes");
            InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
            instance.welcome("zhangsan");
        }
    }
    
    • 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

    2.运行后生成的三个字节码文件

    代理类:InfoDemo$$EnhancerByCGLIB$$45152a9a.class
    代理类的FastClass:

    InfoDemo$$EnhancerByCGLIB$$45152a9a$$FastClassByCGLIB$$778b2fd5.class
    
    • 1

    被代理类的FastClass:InfoDemo$$FastClassByCGLIB$$6332e5ad.class

    3.源码追溯

    ①主方法生成代理对象的demo块运行后出现了三个字节码文件,如2所示

    InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
    
    • 1

    newInstance方法的细节:

    public Object newInstance(Object source) {
            target = source;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    InfoDemo

    EnhancerByCGLIB" role="presentation" style="text-align: center; position: relative;">EnhancerByCGLIB
    45152a9a.class有一块静态代码块会初始化一些数据,如下:

        static {
            CGLIB$STATICHOOK1();
        }
    
        static void CGLIB$STATICHOOK1() {
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class var0 = Class.forName("test.InfoDemo$$EnhancerByCGLIB$$45152a9a");
            Class var1;
            Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
            CGLIB$equals$1$Method = var10000[0];
            CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
            CGLIB$toString$2$Method = var10000[1];
            CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
            CGLIB$hashCode$3$Method = var10000[2];
            CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
            CGLIB$clone$4$Method = var10000[3];
            CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
            CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("test.InfoDemo")).getDeclaredMethods())[0];
            CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这里只要记住一点var1赋值过两次,都是在ReflectUtils中findMethods方法赋值的:

    1.Object类
    2.被代理类InfoDemo

    var0是代理类
    MethodProxy.create参数格式:

    var1,var0,方法描述符,var1的方法,var0的方法

    instance.welcome("zhangsan");JVM会调用代理类字节码文件InfoDemo

    EnhancerByCGLIB" role="presentation" style="text-align: center; position: relative;">EnhancerByCGLIB
    45152a9a.classwelcome方法,如下:

        public final void welcome(String var1) {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
            } else {
                super.welcome(var1);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    疑云:
    1.CGLIB$CALLBACK_0从哪来
    2.CGLIB$BIND_CALLBACKS做了什么
    3.intercept方法又是什么
    解答:
    1.CGLIB$CALLBACK_0有可能一开始就应该为空,又或者在到这步之前赋过值,但是细看如下代码,既让对var10000进行判空,那么有极大甚至说90%可能到这步理应为空,所以说我们暂时先走 CGLIB$BIND_CALLBACKS(this);的逻辑。

    if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
    • 1
    • 2
    • 3
    • 4

    CGLIB$BIND_CALLBACKS(this);

        private static final void CGLIB$BIND_CALLBACKS(Object var0) {
            InfoDemo$$EnhancerByCGLIB$$45152a9a var1 = (InfoDemo$$EnhancerByCGLIB$$45152a9a)var0;
            if (!var1.CGLIB$BOUND) {
                var1.CGLIB$BOUND = true;
                Object var10000 = CGLIB$THREAD_CALLBACKS.get();
                if (var10000 == null) {
                    var10000 = CGLIB$STATIC_CALLBACKS;
                    if (var10000 == null) {
                        return;
                    }
                }
    
                var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    疑云:
    1.CGLIB$BOUND的true or false
    2.有CGLIB$THREAD_CALLBACKS.get();应该在之前哪里调用代理类的set方法set过
    3.CGLIB$STATIC_CALLBACKS又是哪里来的
    解答:
    1.对于整块代码,核心都是当GLIB$BOUND=false才执行,姑且认为是false
    2.CGLIB$THREAD_CALLBACKS.get();在生成代理类的enhancer.create()中set过,因此,转到⑤看下具体逻辑验证。
    3.CGLIB$STATIC_CALLBACKSzaEnhancer类中有生成过方法签名,也许在enhancer.create()的时候执行过。
    enhancer.create()
    一路追到protected Object create(Object key)方法,

    return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
    
    • 1

    先看firstInstance,再追到

    private static void setThreadCallbacks(Class type, Callback[] callbacks) {
            setCallbacksHelper(type, callbacks, "CGLIB$SET_THREAD_CALLBACKS");
        }
    
    • 1
    • 2
    • 3

    深度调用:可以看出执行了名为“CGLIB$SET_THREAD_CALLBACKS”的方法

    private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
            try {
                Method setter = getCallbacksSetter(type, methodName);
                setter.invoke((Object)null, callbacks);
            } catch (NoSuchMethodException var4) {
                throw new IllegalArgumentException(type + " is not an enhanced class");
            } catch (IllegalAccessException var5) {
                throw new CodeGenerationException(var5);
            } catch (InvocationTargetException var6) {
                throw new CodeGenerationException(var6);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们去搜一下代理类的“CGLIB$SET_THREAD_CALLBACKS”方法,果然

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
            CGLIB$THREAD_CALLBACKS.set(var0);
        }
    
    • 1
    • 2
    • 3

    最后我们再回到④
    由于var10000不再是空了,所以直接走下面的逻辑

    var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
    
    • 1

    这段代码的释义:
    var10000引用赋值给Callback[],Callback[]的第一个槽位就是var10000所指对象,再强转成MethodInterceptor引用赋值给CGLIB$CALLBACK_0。因此③中 var10000 = this.CGLIB\$CALLBACK_0;,var10000就有值了,而且最后的值是代理类(var10000多次变幻)。
    ⑥接③var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
    我们说过var10000最终是代理类,所以会执行代理类的intercept方法,如下:
    先输出,再调methodProxy.invokeSuper(o, objects);

        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before method!!!");
            Object value = methodProxy.invokeSuper(o, objects);
            return value;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    methodProxy.invokeSuper(o, objects);
    MethodProxy.FastClassInfo fci = this.fastClassInfo;可以看出是FastClass,再由fci.f2.invoke(fci.i2, obj, args);可知是代理类的FastClass调用invoke方法(fci.f2是代理类的FastClass,那么fci.f1是被代理类的FastClass)

        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                MethodProxy.FastClassInfo fci = this.fastClassInfo;
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    疑惑:
    1.fci.i2的值从哪里来
    解答:
    this.init();

        private void init() {
            if (this.fastClassInfo == null) {
                synchronized(this.initLock) {
                    if (this.fastClassInfo == null) {
                        MethodProxy.CreateInfo ci = this.createInfo;
                        MethodProxy.FastClassInfo fci = new MethodProxy.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;
                    }
                }
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    我们不难发现fci.f1和fci.f2分别是被代理类FastClass和代理类的FastClass,因此这两个方法需分别到对应的class文件去看实现。

    fci.i1 = fci.f1.getIndex(this.sig1);
    fci.i2 = fci.f2.getIndex(this.sig2);
    
    • 1
    • 2

    我们看代理类的FastClass的getIndex方法:
    不难看出,是通过散列值去返回一些整型值赋值给fci.i2。我们重点关注与被代理类方法调用的返回值,例如8,因此,我们明确了fci.i2=8

     public int getIndex(Signature var1) {
            String var10000 = var1.toString();
            switch(var10000.hashCode()) {
            case -1882565338:
                if (var10000.equals("CGLIB$equals$1(Ljava/lang/Object;)Z")) {
                    return 17;
                }
                break;
            case -1870561232:
                if (var10000.equals("CGLIB$findMethodProxy(Lorg/springframework/cglib/core/Signature;)Lorg/springframework/cglib/proxy/MethodProxy;")) {
                    return 9;
                }
                break;
            case -1745842178:
                if (var10000.equals("setCallbacks([Lorg/springframework/cglib/proxy/Callback;)V")) {
                    return 10;
                }
                break;
            case -1641413109:
                if (var10000.equals("newInstance([Lorg/springframework/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                    return 5;
                }
                break;
            case -1457535688:
                if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                    return 15;
                }
                break;
            case -1411842725:
                if (var10000.equals("CGLIB$hashCode$3()I")) {
                    return 19;
                }
                break;
            case -1034266769:
                if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lorg/springframework/cglib/proxy/Callback;)V")) {
                    return 11;
                }
                break;
            case -1025895669:
                if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lorg/springframework/cglib/proxy/Callback;)V")) {
                    return 12;
                }
                break;
            case -988317324:
                if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lorg/springframework/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                    return 6;
                }
                break;
            case -508378822:
                if (var10000.equals("clone()Ljava/lang/Object;")) {
                    return 3;
                }
                break;
            case 233072605:
                if (var10000.equals("welcome(Ljava/lang/String;)V")) {
                    return 8;
                }
                break;
            case 610042816:
                if (var10000.equals("newInstance(Lorg/springframework/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                    return 4;
                }
                break;
            case 1013143764:
                if (var10000.equals("CGLIB$welcome$0(Ljava/lang/String;)V")) {
                    return 16;
                }
                break;
            case 1132856532:
                if (var10000.equals("getCallbacks()[Lorg/springframework/cglib/proxy/Callback;")) {
                    return 14;
                }
                break;
            case 1246779367:
                if (var10000.equals("setCallback(ILorg/springframework/cglib/proxy/Callback;)V")) {
                    return 7;
                }
                break;
            case 1306468936:
                if (var10000.equals("CGLIB$toString$2()Ljava/lang/String;")) {
                    return 18;
                }
                break;
            case 1364367423:
                if (var10000.equals("getCallback(I)Lorg/springframework/cglib/proxy/Callback;")) {
                    return 13;
                }
                break;
            case 1800494055:
                if (var10000.equals("CGLIB$clone$4()Ljava/lang/Object;")) {
                    return 20;
                }
                break;
            case 1826985398:
                if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                    return 0;
                }
                break;
            case 1913648695:
                if (var10000.equals("toString()Ljava/lang/String;")) {
                    return 1;
                }
                break;
            case 1984935277:
                if (var10000.equals("hashCode()I")) {
                    return 2;
                }
            }
    
            return -1;
        }
    
    • 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
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    ⑧接⑦fci.f2.invoke(fci.i2, obj, args);
    已知fci.f2是代理类FastClass,fci.i2的值是8,我们去代理类FastClass找到invoke方法,匹配到8,看执行了什么:
    部分代码:45152a9a是被代理类,匹配到8就是执行了var10000.welcome((String)var3[0]);调用了被代理类的welcome方法,至此,大功告成。

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
            45152a9a var10000 = (45152a9a)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((Callback)var3[0]);
                case 5:
                    return var10000.newInstance((Callback[])var3[0]);
                case 6:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 7:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 8:
                    var10000.welcome((String)var3[0]);
                    return null;
    
    • 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

    4.学到了什么?

    1. jvm运行的是字节码,很多东西源码时不够具体的。
    2. 追踪源码发现方法没有实现的时候,也许是运行是生成的字节码文件实现了这个方法。
    3. cglib的代理类有两个,结合着看。
    4. 学会赌代码逻辑,就像③一样。
    5. 调用链:代理类->intercept方法->invokeSuper方法->代理类FastClass调用被代理类的方法(也就成为了网传八股文版本:代理类调用被代理类的方法实际还是代理类内部的被代理类示例调用本身的方法)
  • 相关阅读:
    【重要】Heygen订阅指南和用法详解!让照片学说话?一张照片变演讲?Heygen订阅值得吗?
    JMETER 时间函数使用
    CVE-2023-1454:Jeecg-Boot SQL注入漏洞复现
    图论27(Leetcode721账户合并)
    美丽修行的“数字化修行”记
    计算机专业毕业设计项目推荐10-饮食搭配平台(Go+微信小程序+Mysql)
    复习leetcode第二题:两数相加
    STM32-串口通信波特率计算以及寄存器的配置详解
    【Vivado使用】从0开始 综合后生成门级网表
    LabVIEW学习笔记十二:分隔栏详解
  • 原文地址:https://blog.csdn.net/m0_48333563/article/details/126221624