JAVA動態(tài)代理

從靜態(tài)代理開始

假設(shè)我們有個業(yè)務(wù)類,類的接口和實現(xiàn)是這么定義的:

public interface Player {
    void play();
}
public PlayerImpl implements Player {
    @Override
    public void play(){
        System.out.println("playing...");
    }
}

如果我想在玩游戲之前和游戲結(jié)束之后,記錄日志,可是不允許改動PlayerImpl的代碼,該怎么辦呢?
這時候可以構(gòu)造一個代理類來做這件事情:

public PlayerProxy implements Player {
    private Player player;
    public PlayerProxy(){
        this.player = new PlayerImpl();
    }
    @Override
    public void play(){
        beforeLog();
        player.play();
        afterLog();
    }

    private void beforeLog(){
        System.out.println("init...");
    }
    private void afterLog(){
        System.out.println("destroy...");
    }
}

這就是靜態(tài)代理,我們可以在不改動源碼的情況下,通過代理對類功能進(jìn)行擴(kuò)展,很符合開閉原則吧:)

動態(tài)代理

靜態(tài)代理的方式很直觀,但是缺點也很明顯——它只能針對特定類進(jìn)行代理,擴(kuò)展性很差,如果有一百個類要代理,難道要寫一百個代理類嗎?顯然我們應(yīng)該擁有更加通用和高效的解決方案,就是動態(tài)代理啦。

JDK動態(tài)代理

JDK動態(tài)代理,需要實現(xiàn)InvocationHandler接口;下面我們用JDK代理的方式重寫上面的代理類:

public PlayerProxyFactory implements InvocationHandler {

    private Object target;

    public PlayerProxyFactory(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    public Object buildProxy(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }
    // 省略beforeLog和afterLog方法
}

實際使用:

public static void main(String[] args) {
    Player player = new PlayerImpl();
    // 構(gòu)造實現(xiàn)
    PlayerProxyFactory proxy = new PlayerProxyFactory(player);
    // 生成代理
    Player playerProxy = (Player) proxy.buildProxy();
    playerProxy.play();
}

可以看出來,動態(tài)代理可以通過接口自動構(gòu)造代理類(其實就是一個工廠)。

cglib動態(tài)代理

JDK InvocationHandler雖好,卻仍有一個缺陷,無法代理未實現(xiàn)接口的類。如果剛好有這樣的需求,該怎么解決呢?這就得靠cglib了。
cglib是一個在運行期動態(tài)生成字節(jié)碼的工具。我承認(rèn)我第一次看到這個介紹的時候被嚇到了,不過撇開原理不說,使用起來與InvocationHandler非常類似:

public class CGLibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target, args);
        after();
        return result;
    }
    public <T> T getProxy(Class<T> clazz) {
        return (T) Enhancer.create(clazz, this);
    }
    // 省略beforeLog和afterLog方法
}

實際使用:

public static void main(String[] args) {
    Player playerProxy = new CGLibProxy().getProxy(PlayerImpl.class);
    playerProxy.play();
}

JAVASSIST

Javassist是一個開源的分析、編輯和創(chuàng)建Java字節(jié)碼的類庫。是由東京工業(yè)大學(xué)的數(shù)學(xué)和計算機(jī)科學(xué)系的 Shigeru Chiba (千葉 滋)所創(chuàng)建的。它已加入了開放源代碼JBoss 應(yīng)用服務(wù)器項目,通過使用Javassist對字節(jié)碼操作為JBoss實現(xiàn)動態(tài)"AOP"框架。

Javassist是一個十分簡單粗暴的工具,它的用法簡單來看是這樣的:

public static void main(String[] args) throws Exception {  
    ClassPool pool = ClassPool.getDefault();  
    //創(chuàng)建Programmer類       
    CtClass cc= pool.makeClass("com.samples.Programmer");  
    //定義code方法  
    CtMethod method = CtNewMethod.make("public void code(){}", cc);  
    //插入方法代碼  
    method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");  
    cc.addMethod(method);  
    //保存生成的字節(jié)碼  
    cc.writeFile("f://myProxy");
}

這么靈活,寫個代理還不是分分鐘......

JAVA動態(tài)代理比較

JDK動態(tài)代理 CGLIB JAVASSIST
實現(xiàn)原理 利用反射機(jī)制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理 利用asm開源包,對代理對象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理 通過修改字節(jié)碼生成類來處理
目標(biāo)類是否需要實現(xià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)容

  • 基礎(chǔ):class文件簡介及加載流程 Java編譯器編譯好Java文件之后,產(chǎn)生.class 文件在磁盤中。這種cl...
    jiangmo閱讀 507評論 0 1
  • 在工作之余看一些優(yōu)秀源碼的時候發(fā)現(xiàn)很多地方使用了動態(tài)代理,所以抽了一些時間對java的動態(tài)代理深入熟悉一下,這篇文...
    半支鉛筆半塊橡皮閱讀 560評論 0 3
  • 代理模式 解說:給某一個對象提供一個代理,并由代理對象控制對原對象的引用; 代理模式需要以下幾個角色: 1 主題:...
    凌渡冰閱讀 412評論 0 1
  • 如何使用動態(tài)代理? 參照上面的例子,我們可以知道要實現(xiàn)動態(tài)代理需要做兩方面的工作。 首先需要新建一個類,并且這個類...
    java部落閱讀 2,200評論 1 16
  • 連續(xù)一周了,她都是在晚上12點出門,凌晨4點回家,輕輕的躺在他身邊。她以為他不知道,他在等她坦白。。。 噠的一聲輕...
    邈邈筆記閱讀 301評論 0 0

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