聊聊cglib动态代理遇到的坑

简介

cglib是另外一种动态代理的方法,他和jdk动态代理的实现是有区别的,我们在之前见过jdk动态代理类是必须实现了接口的,而cglib不需要实现接口,但是必须保证类不含有final关键字,否则是无法代理的。 本文是从个人不小心遇到的cglib的死循环问题从而展开的分析。

cglib案例

下面我们来展示一个cglib的死循环案例。首先是要被代理的类,还是和常规的一样,声明自己的方法就行,但是要确保类和方法没有被final关键字修饰。用final关键字修饰类会直接报异常,但是修饰方法不会抛异常,但是此方法不会被代理,但是不影响其他方法被代理。

public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :" + person);
    }
}
复制代码

下面是具体的代理类实现

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.invoke(o, objects);
        //Object value = methodProxy.invoke(this.target, objects);
        //Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }
   public static void main(String[] args) {
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
      InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
      instance.welcome("zhangsan");

    }
}
复制代码

和我们的jdk动态代理看起来十分相似,只是两个类实现的接口不同,并且生成对象的方法也不同。这里非常坑的是invoke方法和invokeSuper的区别,如果是用invoke方法一定要使用被代理的对象也就是上文中的target,而如果调用invokeSuper方法,则一定要使用被代理后的o对象。

上述这个例子就会引发死循环,导致StackOverflowFlow,嘿嘿,学没有,栈溢出的场景

具体为什么会这样,可以先思考一下,后面我们在源码实现中再去讲解。现在我们先看一下运行结果

...
before method!!!
before method!!!
before method!!!
Exception in thread "main" java.lang.StackOverflowError
	at java.nio.CharBuffer.<init>(CharBuffer.java:281)
	at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70)
	at java.nio.CharBuffer.wrap(CharBuffer.java:373)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.write(PrintStream.java:526)
	at java.io.PrintStream.print(PrintStream.java:669)
	at java.io.PrintStream.println(PrintStream.java:806)
	at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30)
	at com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7.welcome(<generated>)
	at com.eumji.proxy.cglib.InfoDemo$$FastClassByCGLIB$$2e560a7d.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...
复制代码

此处只展示部分的效果,具体可以自己试一下。

假如我们换成其余他两条语句将会是正确的输出,具体结果如下

before method!!!
welcome :zhangsan
复制代码

原理解析

要想弄清楚的这到底是怎么回事,首先我们要看一下cglib代理后的类是怎样的,要想生成代理类的文件,我们只需要在我们的main方法中取消掉这句方法的注释

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\classes");

就会在D盘的classes文件下生成对应的代理class文件,需要注意的是生成的代理class是有三个的,我们首先介绍一下我们最关心的InfoDemo代理类,其他的稍后合适的时机在描述其他两个。

InfoDemo反编译代码

其实就是将class文件直接拖到IDEA中

public class InfoDemo$$EnhancerByCGLIB$$870a84d7 extends InfoDemo implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.eumji.proxy.cglib.InfoDemo")).getDeclaredMethods())[0];
        CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
    }

    final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }

    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == 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);
        }
    }
  ...

}

复制代码

看代码就可以看出来cglib还是很复杂的,现在我们暂且可以看一下我们关心的welcome方法,从上面的代码中可以看到cglib是会为被代理类的方法同时生成两个代理方法的,一个是同名的welcome方法CGLIB$welcome$0方法

1.CGLIB$welcome$0方法直接调用被代理的方法,也就是啥都没干。

2.welcome方法首先判断有没有设置callback,很明显我们在代码中有设置即为CglibInfoProxy,所以就会调用CglibInfoProxy.intercept方法。

本来想分析一波生成代理类的过程,看了一下有点复杂,暂时就不分析了。。。。

invokeSuper方法

前面也提及了invoke和invokeSuper方法稍不注意就会出问题的问题,在这里我们从代码的层面去追踪一下,产生问题的原因。

我们看一下代理类方法invokeSuper的执行流程

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init(); //初始化fastInfo
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    }
}
复制代码

invokeSuper在这里主要的作用就是初始化fastClassInfo。

init方法

private void init() {
    if (this.fastClassInfo == null) {
        Object var1 = this.initLock;
        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;
            }
        }
    }
}
复制代码

上面的方法主要是加载methodProxy.FastClassInfo。ci是之前就初始化好的,其中c1指的就是被代理的类InfoDemo,c2则是com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$efe38465这个代理类。

然后生成对应的f1和f2以及方法的下标i1和i2,i1和i2对应的就是在最前面所说的welcome方法CGLIB$welcome$0方法,后面代码可以看出。

而f1则是对应InfoDemo$$FastClassByCGLIB$$2e560a7d代理类,f2则对应InfoDemo$$EnhancerByCGLIB$$efe38465$$FastClassByCGLIB$$38345933代理类。这些都可以在生成的代理class中去查看。

当然这里并没有说到底什么生成的,有兴趣的可以自己看一下字节码是怎么生成的。个人没太看懂。

invoke和invokeSuper区别

为什么要生成两个代理类f1和f2,我相信你看过之前的方法应该注意到了,在上面我们提及到了invoke方法和invokeSuper方法,我们来对比一下invoke方法和invokeSuper方法的区别

public Object invoke(Object obj, Object[] args) throws Throwable {
  this.init();
  MethodProxy.FastClassInfo fci = this.fastClassInfo;
  return fci.f1.invoke(fci.i1, obj, args);
}
复制代码

我们可以看到invoke使用的是f1.invoke方法,而invokeSuper则是使用f2.invoke方法。

首先看一下f1对应的invoke方法逻辑

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

        try {
            switch(var10001) {
            case 0:
                var10000.welcome((String)var3[0]);
                return null;
                ....
}
复制代码

直接调用InfoDemo对象的welcome方法。

所以这也就能解释为什么我们之前会发生循环调用invoke的方法了,因为我们传入的var2是InfoDemo的代理对象,看最前面的代理类代码就可以看出,又会回到invoke方法,造成死循环。

再看一下f2中对应invoke的实现

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

        try {
            switch(var10001) {
             ....
                var10000.CGLIB$finalize$1();
                return null;
            case 16:
                var10000.CGLIB$welcome$0((String)var3[0]);
                return null;
           ....
    }
复制代码

因为我们此时我们传入的var2是InfoDemo代理对象,所以最终会调用代理类中的CGLIB$welcome$0方法。

小结

只是一次失败的源码分析尝试,不过弄清楚了造成调用死循环的原因,只能说cglib比jdk的动态代理复杂很多,主要体现在生成代码的逻辑和生成的代码上,还有待深入的学习。

而且是有两种invoke方法即invoke和invokeSuper方法,所以使用的时候必须要谨慎。

结语

本文出自个人笔记,如有表述不当或者纰漏的地方欢迎指正。

与君共勉!!!

简介

cglib是另外一种动态代理的方法,他和jdk动态代理的实现是有区别的,我们在之前见过jdk动态代理类是必须实现了接口的,而cglib不需要实现接口,但是必须保证类不含有final关键字,否则是无法代理的。

cglib案例

下面我们来展示一个cglib的死循环案例。首先是要被代理的类,还是和常规的一样,声明自己的方法就行,但是要确保类和方法没有被final关键字修饰。用final关键字修饰类会直接报异常,但是修饰方法不会抛异常,但是此方法不会被代理,但是不影响其他方法被代理。

public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :" + person);
    }
}
复制代码

下面是具体的代理类实现

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.invoke(o, objects);
        //Object value = methodProxy.invoke(this.target, objects);
        //Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }
   public static void main(String[] args) {
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
      InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
      instance.welcome("zhangsan");

    }
}
复制代码

和我们的jdk动态代理看起来十分相似,只是两个类实现的接口不同,并且生成对象的方法也不同。这里非常坑的是invoke方法和invokeSuper的区别,如果是用invoke方法一定要使用被代理的对象也就是上文中的target,而如果调用invokeSuper方法,则一定要使用被代理后的o对象。

上述这个例子就会引发死循环,导致StackOverflowFlow,嘿嘿,学没有,栈溢出的场景

具体为什么会这样,可以先思考一下,后面我们在源码实现中再去讲解。现在我们先看一下运行结果

...
before method!!!
before method!!!
before method!!!
Exception in thread "main" java.lang.StackOverflowError
	at java.nio.CharBuffer.<init>(CharBuffer.java:281)
	at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70)
	at java.nio.CharBuffer.wrap(CharBuffer.java:373)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.write(PrintStream.java:526)
	at java.io.PrintStream.print(PrintStream.java:669)
	at java.io.PrintStream.println(PrintStream.java:806)
	at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30)
	at com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7.welcome(<generated>)
	at com.eumji.proxy.cglib.InfoDemo$$FastClassByCGLIB$$2e560a7d.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...
复制代码

此处只展示部分的效果,具体可以自己试一下。

假如我们换成其余他两条语句将会是正确的输出,具体结果如下

before method!!!
welcome :zhangsan
复制代码

原理解析

要想弄清楚的这到底是怎么回事,首先我们要看一下cglib代理后的类是怎样的,要想生成代理类的文件,我们只需要在我们的main方法中取消掉这句方法的注释

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\classes");

就会在D盘的classes文件下生成对应的代理class文件,需要注意的是生成的代理class是有三个的,我们首先介绍一下我们最关心的InfoDemo代理类,其他的稍后合适的时机在描述其他两个。

InfoDemo反编译代码

其实就是将class文件直接拖到IDEA中

public class InfoDemo$$EnhancerByCGLIB$$870a84d7 extends InfoDemo implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.eumji.proxy.cglib.InfoDemo")).getDeclaredMethods())[0];
        CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
    }

    final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }

    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == 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);
        }
    }
  ...

}

复制代码

看代码就可以看出来cglib还是很复杂的,现在我们暂且可以看一下我们关心的welcome方法,从上面的代码中可以看到cglib是会为被代理类的方法同时生成两个代理方法的,一个是同名的welcome方法CGLIB$welcome$0方法

1.CGLIB$welcome$0方法直接调用被代理的方法,也就是啥都没干。

2.welcome方法首先判断有没有设置callback,很明显我们在代码中有设置即为CglibInfoProxy,所以就会调用CglibInfoProxy.intercept方法。

本来想分析一波生成代理类的过程,看了一下有点复杂,暂时就不分析了。。。。

invokeSuper方法

前面也提及了invoke和invokeSuper方法稍不注意就会出问题的问题,在这里我们从代码的层面去追踪一下,产生问题的原因。

我们看一下代理类方法invokeSuper的执行流程

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init(); //初始化fastInfo
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    }
}
复制代码

invokeSuper在这里主要的作用就是初始化fastClassInfo,然后通过fastClassInfo去调用目标方法。

init方法

此方法也有比较关键的地方,因为生成了代理类f1和f2。

private void init() {
    if (this.fastClassInfo == null) {
        Object var1 = this.initLock;
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                fci.f1 = helper(ci, ci.c1); //fastclass代理类生成
                fci.f2 = helper(ci, ci.c2);//fastclass代理类生成
                fci.i1 = fci.f1.getIndex(this.sig1); //获取下标
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null;
            }
        }
    }
}
复制代码

上面的方法主要是加载methodProxy.FastClassInfo。ci是之前就初始化好的,其中c1指的就是被代理的类InfoDemo,c2则是com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$efe38465这个代理类。

然后生成对应的f1和f2以及方法的下标i1和i2,i1和i2对应的就是在最前面所说的welcome方法CGLIB$welcome$0方法,后面代码可以看出。

而f1则是对应InfoDemo$$FastClassByCGLIB$$2e560a7d代理类,f2则对应InfoDemo$$EnhancerByCGLIB$$efe38465$$FastClassByCGLIB$$38345933代理类。这些都可以在生成的代理class中去查看。

当然这里并没有说到底什么生成的,有兴趣的可以自己看一下字节码是怎么生成的。个人没太看懂。

invoke和invokeSuper区别

为什么要生成两个代理类f1和f2,我相信你看过之前的方法应该注意到了,在上面我们提及到了invoke方法和invokeSuper方法,我们来对比一下invoke方法和invokeSuper方法的区别

public Object invoke(Object obj, Object[] args) throws Throwable {
  this.init();
  MethodProxy.FastClassInfo fci = this.fastClassInfo;
  return fci.f1.invoke(fci.i1, obj, args);
}
复制代码

我们可以看到invoke使用的是f1.invoke方法,而invokeSuper则是使用f2.invoke方法。

首先看一下f1对应的invoke方法逻辑

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

        try {
            switch(var10001) {
            case 0:
                var10000.welcome((String)var3[0]);
                return null;
                ....
}
复制代码

直接调用InfoDemo对象的welcome方法。

所以这也就能解释为什么我们之前会发生循环调用invoke的方法了,因为我们传入的var2是InfoDemo的代理对象,看最前面的代理类代码就可以看出,又会回到invoke方法,造成死循环。

再看一下f2中对应invoke的实现

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

        try {
            switch(var10001) {
             ....
                var10000.CGLIB$finalize$1();
                return null;
            case 16:
                var10000.CGLIB$welcome$0((String)var3[0]);
                return null;
           ....
    }
复制代码

因为我们此时我们传入的var2是InfoDemo代理对象,所以最终会调用代理类中的CGLIB$welcome$0方法。

小结

只是一次失败的源码分析尝试,不过弄清楚了造成调用死循环的原因,只能说cglib比jdk的动态代理复杂很多,主要体现在生成代码的逻辑和生成的代码上,还有待深入的学习。

而且是有两种invoke方法即invoke和invokeSuper方法,所以使用的时候必须要谨慎。

结语

本文出自个人笔记,如有表述不当或者纰漏的地方欢迎指正。

与君共勉!!!

转载于:https://juejin.im/post/5a8f750af265da4e983f2369

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值