zoukankan      html  css  js  c++  java
  • CgLib动态代理学习【Spring AOP基础之一】

      如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习【Spring AOP基础之一】

      由于Java动态代理Proxy.newProxyInstance()的时候会发现其参数类型是ClassLoader classLoader, Class<?>[] interface, InvocationHandler handler, 只支持根据接口实现代理类,如果所有代码都是自己掌控,当然没有问题。所有的业务逻辑均抽象出接口,然后所有的业务类实现接口,这样所有的业务类均可以使用Java动态代理作为AOP编程的底层支持;但是,假设如果我们想要给一个外部依赖的类添加AOP,并且外部的类并没有实现接口,此时Java动态代理就无能为力了,此时就需要CgLib出场了。

      业务逻辑类(无接口实现)

    public class EasyClass {
        public void easyMethod() {
            System.out.println("这是一个非常简单的方法");
        }
    }
    

      如果想要使用CgLib给该业务逻辑类创建代理,并且在业务方法的前后等位置增加一些处理逻辑,需要实现net.sf.cglib.proxy.MethodInterceptor接口,并且重写它的intercept方法(代理类中额外的逻辑实现需要在这个函数中实现,是否调用原有类中的方法也需要在这里体现),下面简单在原有方法前后增加一些逻辑;

    public class EasyIntercepter 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;
        }
    }
    

      接下来使用net.sf.cglib.proxy.Enhancer生成相应的代理类即可

    public class EasyProxyTest {
        public static void main(String[] args) {
    
            EasyClass easyClass =  (EasyClass)Enhancer.create(
                    EasyClass.class,
                    null, new EasyIntercepter() );
            easyClass.easyMethod();
    
            //another method
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(EasyClass.class);
            enhancer.setCallback(new EasyIntercepter());
            ((EasyClass)enhancer.create()).easyMethod();
        }
    }
    

      输出内容如下:

      从输出内容来看,已经简单的在原有的业务方法前后添加的处理逻辑,所以这个技术就可以用来当做AOP底层生成的技术之一,Spring AOP也正是用这个技术来生成,当配置文件中启用了cglib生成代理类,或者是需要代理的对象没有实现接口,Spring均会使用cglib生成代理类。但是需要使用maven或者gradle引入cglib的引用。

      其方法应该类似于JDK动态代理。该方法是生成原有类的子类,并使用MethodInterceptor截取每个方法的调用。

      将上面生成的代理类反向编译如下:

    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    
    public class EasyClass$$EnhancerByCGLIB$$48da2939 extends EasyClass 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$easyMethod$0$Method;
        private static final MethodProxy CGLIB$easyMethod$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.proxy.EasyClass$$EnhancerByCGLIB$$48da2939");
            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$easyMethod$0$Method = ReflectUtils.findMethods(new String[]{"easyMethod", "()V"}, (var1 = Class.forName("net.sf.cglib.proxy.EasyClass")).getDeclaredMethods())[0];
            CGLIB$easyMethod$0$Proxy = MethodProxy.create(var1, var0, "()V", "easyMethod", "CGLIB$easyMethod$0");
        }
    
        final void CGLIB$easyMethod$0() {
            super.easyMethod();
        }
    
        public final void easyMethod() {
            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$easyMethod$0$Method, CGLIB$emptyArgs, CGLIB$easyMethod$0$Proxy);
            } else {
                super.easyMethod();
            }
        }
    
        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).booleanValue();
            } 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 -1577766638:
                if(var10000.equals("easyMethod()V")) {
                    return CGLIB$easyMethod$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 EasyClass$$EnhancerByCGLIB$$48da2939() {
            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) {
            EasyClass$$EnhancerByCGLIB$$48da2939 var1 = (EasyClass$$EnhancerByCGLIB$$48da2939)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);
            EasyClass$$EnhancerByCGLIB$$48da2939 var10000 = new EasyClass$$EnhancerByCGLIB$$48da2939();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Callback var1) {
            CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
            EasyClass$$EnhancerByCGLIB$$48da2939 var10000 = new EasyClass$$EnhancerByCGLIB$$48da2939();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
            CGLIB$SET_THREAD_CALLBACKS(var3);
            EasyClass$$EnhancerByCGLIB$$48da2939 var10000 = new EasyClass$$EnhancerByCGLIB$$48da2939;
            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();
        }
    }
    

      

      上述方法中CGLIB$CALLBACK_0就是设置的callback函数,及实现的MethodInceptor接口的逻辑类,所以当调用easyMethod的时候,如果代理类中存在实现MethodInceptor接口的逻辑类,调用其intercept方法,其实这个地方的原理和JDK动态代理有些相似。

      反编译的方法是git clone cglib的工程文件,找到Enhancer类中创建代理类byte[]的位置,将byte[]数组写成.class文件,然后反编译.class文件。因为和JDK动态代理一样,运行时动态生成类都是动态生成byte[]类结构,然后通过defineClass加载到虚拟机中进一步得到对象。

      目前不知道如何从一个class类型得到其byte[]文件,所以只能采取上面的方法;关于这个我查阅到两个相关问题,感觉没有很简单的方法,大多回答是基于已有.class文件,但是这里只有动态的class类型,并没有生成.class文件。

      https://stackoverflow.com/questions/7980133/converting-a-given-class-lets-say-java-lang-object-to-a-byte-array-is-it-po

      https://stackoverflow.com/questions/2036108/convert-class-object-to-bytes

  • 相关阅读:
    python 字符串替换功能 string.replace()可以用正则表达式,更优雅
    windows 10 如何设定计划任务自动执行 python 脚本?
    win10 设定计划任务时提示所指定的账户名称无效,如何解决?
    pandas数据分析输出excel产生文本形式存储的百分比数据,如何处理?
    coinmarketcap前20之cardano卡尔达诺(ADA艾达币)
    【SpringMVC】参数绑定
    【SpringMVC】入门
    【Hibernate】 二级缓存及查询缓存
    【Hibernate】事务处理
    【Hibernate】抓取策略
  • 原文地址:https://www.cnblogs.com/weixliu/p/7363053.html
Copyright © 2011-2022 走看看