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); }