1. 程式人生 > >CgLib動態代理學習【Spring AOP基礎之一】

CgLib動態代理學習【Spring AOP基礎之一】

div 目前 .get 不知道 ctu get() 內容 想要 外部依賴

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

CgLib動態代理學習【Spring AOP基礎之一】