第6講 | 動態(tài)代理是基于什么原理?

談?wù)?Java 反射機制,動態(tài)代理是基于什么原理?

典型回答

反射機制是 Java 語言提供的一種基礎(chǔ)功能,賦予程序在運行時自?。╥ntrospect)的能力,通過反射我們可以直接操作類或者對象,比如獲取某個對象的類定義,獲取類聲明的屬性和方法,調(diào)用方法或者構(gòu)造對象,甚至可以運行時修改類定義。通過運行時操作元數(shù)據(jù)或?qū)ο螅琂ava 可以靈活地操作運行時才能確定的信息。

動態(tài)代理是一種方便運行時動態(tài)構(gòu)建代理、動態(tài)處理代理方法調(diào)用的機制。

實現(xiàn)動態(tài)代理的方式很多,比如 JDK 自身提供的動態(tài)代理,就是主要利用了上面提到的反射機制。還有利用更高性能的字節(jié)碼操作機制,類似 ASM、cglib(基于ASM)、Javassist 等。很多繁瑣的重復(fù)編程,都可以被動態(tài)代理機制優(yōu)雅地解決。

反射機制及其演進(jìn)

查看java.lang或java.lang.reflect包下的相關(guān)抽象,Class、Field、Method、Constructor等,這些完全就是我們?nèi)ゲ僮黝惡蛯ο蟮脑獢?shù)據(jù)對應(yīng)。

AccessibleObject.setAccessible(boolean)可以運行時修改成員的訪問權(quán)限。

但是,在 Java 9 以后,這個方法的使用可能會存在一些爭議。因為 Jigsaw 項目新增的模塊化系統(tǒng),出于強封裝性的考慮,對反射訪問進(jìn)行了限制。Jigsaw 引入了所謂 Open 的概念,只有當(dāng)被反射操作的模塊和指定的包對反射調(diào)用者模塊Open,才能使用 setAccessible;否則,被認(rèn)為不合法操作。如果我們的實體類是定義在模塊里面,我們需要在模塊描述符中明確聲明:

module MyEntities {
    // Open for reflection
    opens com.mycorp to java.persistence;
}

動態(tài)代理

首先,它是一個代理機制。很多動態(tài)代理場景,可以看作是裝飾器(Decorator)模式的應(yīng)用。通過代理可以讓調(diào)用者與實現(xiàn)者之間解耦。

代理的發(fā)展經(jīng)歷了靜態(tài)到動態(tài)的過程,源于靜態(tài)代理引入的額外工作。

JDK 動態(tài)代理的一個簡單例子。下面只是加了一句 print,在生產(chǎn)系統(tǒng)中,我們可以輕松擴展類似邏輯進(jìn)行診斷、限流等。

public class MyDynamicProxy {
    public static  void main (String[] args) {
        HelloImpl hello = new HelloImpl();
        MyInvocationHandler handler = new MyInvocationHandler(hello);
        // 構(gòu)造代碼實例
        Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);
        // 調(diào)用代理方法
        proxyHello.sayHello();
    }
}
interface Hello {
    void sayHello();
}
class HelloImpl implements  Hello {
    @Override
    public void sayHello() {
        System.out.println("Hello World");
    }
}
 class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("Invoking sayHello");
        Object result = method.invoke(target, args);
        return result;
    }
}

從 API 設(shè)計和實現(xiàn)的角度,這種實現(xiàn)仍然有局限性,因為它是以接口為中心的,相當(dāng)于添加了一種對于被調(diào)用者沒有太大意義的限制。我們實例化的是 Proxy 對象,而不是真正的被調(diào)用類型,這在實踐中還是可能帶來各種不便和能力退化。

如果被調(diào)用者沒有實現(xiàn)接口,可以使用cglib方式。

Spring AOP 支持兩種模式的動態(tài)代理,JDK Proxy或者cglib

cglib 動態(tài)代理采取的是創(chuàng)建目標(biāo)類的子類的方式,因為是子類化,我們可以達(dá)到近似使用被調(diào)用者本身的效果。

JDK Proxy 的優(yōu)勢:

  1. 最小化依賴關(guān)系,減少依賴意味著簡化開發(fā)和維護(hù),JDK 本身的支持,可能比 cglib 更加可靠。
  2. 平滑進(jìn)行 JDK 版本升級,而字節(jié)碼類庫通常需要進(jìn)行更新以保證在新版jdk中能使用。
  3. 代碼實現(xiàn)簡單。

基于類似 cglib 框架的優(yōu)勢:

  1. 有的時候調(diào)用目標(biāo)可能不便實現(xiàn)額外接口,從某種角度看,限定調(diào)用者實現(xiàn)接口是有些侵入性的實踐。
  2. 只操作我們關(guān)心的類,而不必為其他相關(guān)類增加工作量。
  3. 高性能

動態(tài)代理應(yīng)用非常廣泛,它完美符合 Spring AOP 等切面編程??梢钥醋魇菍?OOP 的一個補充,因為 OOP 對于跨越不同對象或類的分散、糾纏邏輯表現(xiàn)力不夠,比如在不同模塊的特定階段做一些事情,類似日志、用戶鑒權(quán)、全局性異常處理、性能監(jiān)控,甚至事務(wù)處理等。

AOP 通過(動態(tài))代理機制可以讓開發(fā)者從這些繁瑣事項中抽身出來,大幅度提高了代碼的抽象程度和復(fù)用度。

最后編輯于
?著作權(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)容