cglib invoke 和 invokeSuper 可用的组合

Posted yangmengdx3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cglib invoke 和 invokeSuper 可用的组合相关的知识,希望对你有一定的参考价值。

深入字节码理解invokeSuper无限循环的原因中,我们理解的cglib的原理和其中一个合理的调用方式。但是这个调用方式是基于类的,对所有实例生效。实际场景中,我们可能只是希望代理某个具体的实例,而且这个实例会有自己的特有属性。这个时候要怎么做呢?

 

public class CglibDynamicProxyDemo {

    static class SampleClass {
        public void print(){
            System.out.println("hello world");
        }
    }

    public static void main(String[] args) {
        SampleClass sampleClass = new SampleClass();
        SampleClass sample = createCglibDynamicProxy(sampleClass);
        sample.print();
    }

    private static SampleClass createCglibDynamicProxy(SampleClass delegate) {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(new CglibInterceptor(delegate));
        enhancer.setSuperclass(SampleClass.class);
        return (SampleClass) enhancer.create();
    }

    private static class CglibInterceptor implements MethodInterceptor {

        private Object delegate;

        public CglibInterceptor(Object delegate) {
            this.delegate = delegate;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, net.sf.cglib.proxy.MethodProxy methodProxy) throws Throwable {
            return methodProxy.invokeSuper(delegate, objects);
        }
    }
}

 

通常我们会生成一个拦截器类,然后把实例传递进去,调用的时候使用被代理的对象。

执行代码:

Exception in thread "main" java.lang.ClassCastException: com.ym.materials.proxy.CglibDynamicProxyDemo$SampleClass cannot be cast to com.ym.materials.proxy.CglibDynamicProxyDemo$SampleClass$$EnhancerByCGLIB$$db74855e
	at com.ym.materials.proxy.CglibDynamicProxyDemo$SampleClass$$EnhancerByCGLIB$$db74855e$$FastClassByCGLIB$$6a2a8700.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
	at com.ym.materials.proxy.CglibDynamicProxyDemo$CglibInterceptor.intercept(CglibDynamicProxyDemo.java:42)
	at com.ym.materials.proxy.CglibDynamicProxyDemo$SampleClass$$EnhancerByCGLIB$$db74855e.print(<generated>)
	at com.ym.materials.proxy.CglibDynamicProxyDemo.main(CglibDynamicProxyDemo.java:22)

异常了,why?

通过前面的分析,我们知道invokeSuper调用fci.f2.invoke(fci.i2, obj, args),使用的是第三个生成类SampleClass$$EnhancerByCGLIB$$8ed28f$$FastClassByCGLIB$$520b645b,方法签名是:CGLIB$test$0

通过方法签名的hashcode映射后得到索引为16

 6         case -1659809612:
 7             if(var10000.equals("CGLIB$test$0()V")) {
 8                 return 16;
 9             }
10             break;
 
 1 public class SampleClass$$EnhancerByCGLIB$$8ed28f$$FastClassByCGLIB$$520b645b extends FastClass {
 2 
 3     public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
 4         8ed28f var10000 = (8ed28f)var2;
 5         int var10001 = var1;
 6 
 7         try {
 8             switch(var10001) {
 9             case 7:
10                 var10000.test();
11                 return null;
12             case 16:
13                 var10000.CGLIB$test$0();
14                 return null;
15         } catch (Throwable var4) {
16             throw new InvocationTargetException(var4);
17         }
18 
19         throw new IllegalArgumentException("Cannot find matching method/constructor");
20     }
21 }

调用的时候,会先进行类型转换。这样问题就来了,我们传入的delegate是一个sampleClasss实例,而不是新生成的对象,所以类型转换出错。所以如果代理具体实例,正确的写法是:

 1    private static class CglibInterceptor implements MethodInterceptor {
 2 
 3         private Object delegate;
 4 
 5         public CglibInterceptor(Object delegate) {
 6             this.delegate = delegate;
 7         }
 8 
 9         @Override
10         public Object intercept(Object o, Method method, Object[] objects, net.sf.cglib.proxy.MethodProxy methodProxy) throws Throwable {
11             return methodProxy.invoke(delegate, objects);
12         }
13     }

 

总结:

cglib动态代理

如果代理的类本身,需要使用

public Object intercept(Object o, Method method, Object[] objects, net.sf.cglib.proxy.MethodProxy methodProxy) throws Throwable {
    return methodProxy.invoke(o, objects);
}

如果代理的是实例,需要使用

public Object intercept(Object o, Method method, Object[] objects, net.sf.cglib.proxy.MethodProxy methodProxy) throws Throwable {
    return methodProxy.invoke(delegate, objects);
}

 



 





以上是关于cglib invoke 和 invokeSuper 可用的组合的主要内容,如果未能解决你的问题,请参考以下文章

cglib invoke 和 invokeSuper 可用的组合

深入理解字节码理解invokeSuper无限循环的原因

cglib测试例子和源码详解

jdk动态代理和cglib动态代理

SpringCloud系列之Feign-4.Feign的动态代理

浅谈静态代理和动态代理