CgLib动态代理学习【Spring AOP基础之一】

来源:互联网 发布:windows截图工具找不到 编辑:程序博客网 时间:2024/06/01 09:47

  如果不了解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

 

阅读全文
0 0
原创粉丝点击