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


  • 相关阅读:
    zookeeper安装教程
    CentOS7服务管理(重启,停止,自动启动命令)
    redis配置文件详解
    阿里云 CentOS7安装redis4.0.9并开启远程访问
    rpm命令
    wget命令
    yum命令
    maven项目debug调试不能够进入源码问题解决
    8-字符串
    7-数组
  • 原文地址:https://www.cnblogs.com/yangmengdx3/p/9221806.html
Copyright © 2020-2023  润新知