JDK動態(tài)代理和cglib動態(tài)代理

回顧一下JDK動態(tài)代理和cglib動態(tài)代理,轉(zhuǎn)自我的BLOG:

http://zeusjava.com/2015/10/13/jdk-dynamic-agent-and-cglib/

閑言少敘,先來說一下什么是代理模式,我們?nèi)ヒ粋€新的地方總是要先找地方住,但是我們?nèi)松夭皇斓恼莆盏馁Y源不多,這時候一般會找中介,中介對房源很熟悉,很快就能為你找到合適的房子,這時候,中介就是一個代理,你就相當(dāng)于是一個委托方。

下面是設(shè)計模式中的代理:

代理模式

代理模式是常用的java設(shè)計模式,他的特征是代理類委托類有同樣的接口,代理類主要負責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關(guān)聯(lián)關(guān)系,一個代理類的對象與一個委托類的對象關(guān)聯(lián),代理類的對象本身并不真正實現(xiàn)服務(wù),而是通過調(diào)用委托類的對象的相關(guān)方法,來提供特定的服務(wù)。

按照代理的創(chuàng)建時期,代理類可以分為兩種:

靜態(tài)代理

由程序員創(chuàng)建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經(jīng)存在了。 

動態(tài)代理

在程序運行時,運用反射機制動態(tài)創(chuàng)建而成。 動態(tài)代理類的字節(jié)碼在程序運行時由Java反射機制動態(tài)生成,無需程序員手工編寫它的源代碼。動態(tài)代理類不僅簡化了編程工作,而且提高了軟件系統(tǒng)的可擴展性,因為Java 反射機制可以生成任意類型的動態(tài)代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態(tài)代理類的能力。 

JDK動態(tài)代理

先來看下JDK源碼中InvocationHandler中invoke()方法

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

JDK源碼中Proxy類的代碼:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    if (h == null) {
        throw new NullPointerException();
    }

    /*     
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass(loader, interfaces);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return cons.newInstance(new Object[] { h });
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
    } catch (InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        throw new InternalError(e.toString());
    }
}

參數(shù)說明:

ClassLoader loader:類加載器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子類實例 

PS:類加載器

在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的實例,ClassLoader實際上對應(yīng)的是類加載器,
在Java中主要有一下三種類加載器:

Booststrap ClassLoader:此加載器采用C++編寫,一般開發(fā)中是看不到的; 
Extendsion ClassLoader:用來進行擴展類的加載,一般對應(yīng)的是jre\lib\ext目錄中的類; 
AppClassLoader:(默認)加載classpath指定的類,是最常使用的是一種加載器。

JDK動態(tài)代理實現(xiàn)步驟

實現(xiàn)InvocationHandler接口

獲得代理對象

public Object getInstance(Object target){
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

回調(diào)函數(shù)

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = null;
    System.out.println("jdk 動態(tài)代理 begin...");
    result = method.invoke(target,args);
    System.out.println("jdk 動態(tài)代理 end...");
    return result;
}

JDK動態(tài)代理缺點:

只能對實現(xiàn)了接口的類進行,沒有實現(xiàn)接口的類不能使用JDK動態(tài)代理。

cglib動態(tài)代理

JDK的動態(tài)代理機制只能代理實現(xiàn)了接口的類,而不能實現(xiàn)接口的類就不能實現(xiàn)JDK的動態(tài)代理,cglib是針對類來實現(xiàn)代理的,他的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現(xiàn)增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。
cglib實現(xiàn)動態(tài)代理的方法和JDK動態(tài)代理類似

實現(xiàn)MethodInterceptor接口

獲得代理對象

public Object getInstance(Object target){
    this.target = target;
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(this.target.getClass());
    //設(shè)置回調(diào)方法
    enhancer.setCallback(this);
    //創(chuàng)建代理對象
    return enhancer.create();
}

設(shè)置回調(diào)方法

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("UserFacadeProxy.intercept begin");
    methodProxy.invokeSuper(o,objects);
    System.out.println("UserFacadeProxy.intercept end");
   return null;
}

Spring AOP原理

java動態(tài)代理是利用反射機制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。而cglib動態(tài)代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理。
SpringAOP動態(tài)代理策略是:

1、如果目標對象實現(xiàn)了接口,默認情況下會采用JDK的動態(tài)代理實現(xiàn)AOP 
2、如果目標對象實現(xiàn)了接口,可以強制使用CGLIB實現(xiàn)AOP 
3、如果目標對象沒有實現(xiàn)了接口,必須采用CGLIB庫,spring會自動在JDK動態(tài)代理和CGLIB之間轉(zhuǎn)換
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容