1. 程式人生 > >cglib invoke 和 invokeSuper 可用的組合

cglib invoke 和 invokeSuper 可用的組合

html hello exc 場景 怎麽 java 總結 ML rgs

在深入字節碼理解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 可用的組合