美文网首页
浅谈cglib动态代理

浅谈cglib动态代理

作者: overflow_e4e4 | 来源:发表于2019-05-08 15:58 被阅读0次

    cglib的使用与解析

    上一篇讲解了java原生的动态代理的使用和部分原理,除了原生的动态代理很多java框架中使用了cglib来做动态代理,so今天准备写一些cglib的使用与解析。

    基本使用规则

    共需要3个类(比jdk原生少了一个,因为被代理类不需要实现接口),还是分三步走:

    • 一个代理类
    public class Car {
    
        public void run() {
            System.out.println("car run");
        }
    }
    
    • 一个自定义的MethodInterceptor
    public class MyMethodInterceptor implements MethodInterceptor {
    
    
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("before");
            Object result = proxy.invokeSuper(obj,args);
            System.out.println("after");
    
            return result;
        }
    }
    
    • 最后一个测试main函数
    public class Demo {
        public static void main(String[] args) {
    
            MyMethodInterceptor interceptor = new MyMethodInterceptor();
            Enhancer e = new Enhancer();
            e.setSuperclass(Car.class);
            e.setCallback(interceptor);
            Car car = (Car) e.create();
            car.run();
        }
    }
    
    • 运行结果
    before
    car run
    after
    
    Process finished with exit code 0
    

    一些细节

    和jdk原生的代理一样我也想通过阅读源码来找到是否能把cglib生成的class文件存到本地。但是明显cglib的源码逻辑更为复杂,我没有在短时间内找到,所以我查了网上其他人的资料的知道了需要设置cglib生成class文件的目录,就像这样:System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\cglib");,实际上得到源class文件后的我也是一脸懵逼,因为他不止一个。事实上一共生成了5个:

    screenshot.png
    因为我的测试demo都放在sampes下而且通过类名可以知道 sampes下三个生成的class大概和我的Car被代理类关联比较大。但这也只是猜想,要搞清楚这五个类分别起的什么左右,带着这个问题去看源码可能更好。

    现在我分别把上面的五个class文件称为class1,class2,...,class5,
    首先我通过断点看到e.create()得到的类是class4

    image.png 。
    而class4反编译后是这样的:
    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package net.sf.cglib.samples;
    
    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Factory;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class Car$$EnhancerByCGLIB$$bbb4952c extends Car implements Factory {
        private boolean CGLIB$BOUND;
        public static Object CGLIB$FACTORY_DATA;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
        private MethodInterceptor CGLIB$CALLBACK_0;
        private static Object CGLIB$CALLBACK_FILTER;
        private static final Method CGLIB$run$0$Method;
        private static final MethodProxy CGLIB$run$0$Proxy;
        private static final Object[] CGLIB$emptyArgs;
        private static final Method CGLIB$equals$1$Method;
        private static final MethodProxy CGLIB$equals$1$Proxy;
        private static final Method CGLIB$toString$2$Method;
        private static final MethodProxy CGLIB$toString$2$Proxy;
        private static final Method CGLIB$hashCode$3$Method;
        private static final MethodProxy CGLIB$hashCode$3$Proxy;
        private static final Method CGLIB$clone$4$Method;
        private static final MethodProxy CGLIB$clone$4$Proxy;
    
        static void CGLIB$STATICHOOK1() {
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class var0 = Class.forName("net.sf.cglib.samples.Car$$EnhancerByCGLIB$$bbb4952c");
            Class var1;
            Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
            CGLIB$equals$1$Method = var10000[0];
            CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
            CGLIB$toString$2$Method = var10000[1];
            CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
            CGLIB$hashCode$3$Method = var10000[2];
            CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
            CGLIB$clone$4$Method = var10000[3];
            CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
            CGLIB$run$0$Method = ReflectUtils.findMethods(new String[]{"run", "()V"}, (var1 = Class.forName("net.sf.cglib.samples.Car")).getDeclaredMethods())[0];
            CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
        }
    
        final void CGLIB$run$0() {
            super.run();
        }
    
        public final void run() {
            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$run$0$Method, CGLIB$emptyArgs, CGLIB$run$0$Proxy);
            } else {
                super.run();
            }
        }
    
        final boolean CGLIB$equals$1(Object var1) {
            return super.equals(var1);
        }
    
        public final boolean equals(Object 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) {
                Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
                return var2 == null ? false : (Boolean)var2;
            } else {
                return super.equals(var1);
            }
        }
    
        final String CGLIB$toString$2() {
            return super.toString();
        }
    
        public final String toString() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
        }
    
        final int CGLIB$hashCode$3() {
            return super.hashCode();
        }
    
        public final int hashCode() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
                return var1 == null ? 0 : ((Number)var1).intValue();
            } else {
                return super.hashCode();
            }
        }
    
        final Object CGLIB$clone$4() throws CloneNotSupportedException {
            return super.clone();
        }
    
        protected final Object clone() throws CloneNotSupportedException {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
        }
    
        public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
            String var10000 = var0.toString();
            switch(var10000.hashCode()) {
            case -919875318:
                if (var10000.equals("run()V")) {
                    return CGLIB$run$0$Proxy;
                }
                break;
            case -508378822:
                if (var10000.equals("clone()Ljava/lang/Object;")) {
                    return CGLIB$clone$4$Proxy;
                }
                break;
            case 1826985398:
                if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                    return CGLIB$equals$1$Proxy;
                }
                break;
            case 1913648695:
                if (var10000.equals("toString()Ljava/lang/String;")) {
                    return CGLIB$toString$2$Proxy;
                }
                break;
            case 1984935277:
                if (var10000.equals("hashCode()I")) {
                    return CGLIB$hashCode$3$Proxy;
                }
            }
    
            return null;
        }
    
        public Car$$EnhancerByCGLIB$$bbb4952c() {
            CGLIB$BIND_CALLBACKS(this);
        }
    
        public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
            CGLIB$THREAD_CALLBACKS.set(var0);
        }
    
        public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
            CGLIB$STATIC_CALLBACKS = var0;
        }
    
        private static final void CGLIB$BIND_CALLBACKS(Object var0) {
            Car$$EnhancerByCGLIB$$bbb4952c var1 = (Car$$EnhancerByCGLIB$$bbb4952c)var0;
            if (!var1.CGLIB$BOUND) {
                var1.CGLIB$BOUND = true;
                Object var10000 = CGLIB$THREAD_CALLBACKS.get();
                if (var10000 == null) {
                    var10000 = CGLIB$STATIC_CALLBACKS;
                    if (CGLIB$STATIC_CALLBACKS == null) {
                        return;
                    }
                }
    
                var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
            }
    
        }
    
        public Object newInstance(Callback[] var1) {
            CGLIB$SET_THREAD_CALLBACKS(var1);
            Car$$EnhancerByCGLIB$$bbb4952c var10000 = new Car$$EnhancerByCGLIB$$bbb4952c();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Callback var1) {
            CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
            Car$$EnhancerByCGLIB$$bbb4952c var10000 = new Car$$EnhancerByCGLIB$$bbb4952c();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
            CGLIB$SET_THREAD_CALLBACKS(var3);
            Car$$EnhancerByCGLIB$$bbb4952c var10000 = new Car$$EnhancerByCGLIB$$bbb4952c;
            switch(var1.length) {
            case 0:
                var10000.<init>();
                CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
                return var10000;
            default:
                throw new IllegalArgumentException("Constructor not found");
            }
        }
    
        public Callback getCallback(int var1) {
            CGLIB$BIND_CALLBACKS(this);
            MethodInterceptor var10000;
            switch(var1) {
            case 0:
                var10000 = this.CGLIB$CALLBACK_0;
                break;
            default:
                var10000 = null;
            }
    
            return var10000;
        }
    
        public void setCallback(int var1, Callback var2) {
            switch(var1) {
            case 0:
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
            default:
            }
        }
    
        public Callback[] getCallbacks() {
            CGLIB$BIND_CALLBACKS(this);
            return new Callback[]{this.CGLIB$CALLBACK_0};
        }
    
        public void setCallbacks(Callback[] var1) {
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
        }
    
        static {
            CGLIB$STATICHOOK1();
        }
    }
    
    

    只看run()方法的话逻辑并不复杂,最终会走到var10000.intercept(this, CGLIB$run$0$Method, CGLIB$emptyArgs, CGLIB$run$0$Proxy);也就是我自己实现的MyMethodInterceptor的intercept方法中。
    剩余的一些东西其实通过看了源码也才理解了1/3了更没法通过语言很好的表述,所以就这次先鸽了。希望之后能结合别人和自己的看法再写一篇。

    一个坑

    最后再讲一下我在写demo调试的时候遇到的一个坑吧
    我是这样写的demo:

    public class MyMethodInterceptor implements MethodInterceptor {
    
    
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
          System.out.println("before");
          Object result = proxy.invokeSuper(obj, args);
          System.out.println("method:" + method);
          System.out.println("obj:" + obj);
          System.out.println("after");
    
          return result;
      }
    }
    

    这里主要是 System.out.println("obj:" + obj);这句话有问题 ,因为这样会调用objhashcode方法,而这个obj就是代理类,代理类的hashcode方法会继续调用MyMethodInterceptor.intercept,这样形成了死循环,最终stackoverflow

    `

    相关文章

      网友评论

          本文标题:浅谈cglib动态代理

          本文链接:https://www.haomeiwen.com/subject/veoeoqtx.html