深入Java-動(dòng)態(tài)代理+源碼分析Proxy、InvocationHandler

先來理一下概念理論

代理三要素

抽象主題角色(Subject)
具體主題角色(RealSubject)
代理主題角色(Proxy)

代理關(guān)系圖

image.png

栗子

Subject:購房需求
RealSubject:小明的購房需求
Proxy: 中介A(只為小明服務(wù))

中介可以幫助或者代理小明做一些事情,比如篩選房源、預(yù)溝通等等,這就是代理的好處,專業(yè)、高效。

但是有以下問題:
1、中介A只為小明服務(wù),如果小紅、小強(qiáng)都要買房,怎么辦呢?
2、小明還有買車需求,也想找中介幫忙,怎么辦呢? 中介A懂房但不懂車。

問題

靜態(tài)代理中,此時(shí)就需要new 新的代理類,無論怎么抽象、怎么封裝,一個(gè)代理類總是能力有限的。
要為每個(gè)目標(biāo)Subject編寫對應(yīng)的代理類,隨著系統(tǒng)龐大,工作量會劇增,并且可維護(hù)性變差。
那么思考,面對變化萬千的subject,如何才能少寫代理類,或者不寫代理類,又能完成代理功能呢?

思考方向:
一個(gè)接口(subject) --》如何得到一個(gè)代理類proxy? 這不是自動(dòng)生成類嗎?
是的,反射、Class

這就是動(dòng)態(tài)代理。

Java的解決方案,動(dòng)態(tài)代理

Java用jdk提供的ProxyInvocationHandler結(jié)合實(shí)現(xiàn)動(dòng)態(tài)代理功能。
先來看一段使用栗子

// 獲取 買房需求 的Class
Class<?> proxyClass = Proxy.getProxyClass(BuyHouse.class.getClassLoader(), BuyHouse.class);

// 通過Class的構(gòu)造器Constructor 動(dòng)態(tài)創(chuàng)建統(tǒng)一的代理服務(wù)Lianjia
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
BuyHouse lianjia = (BuyHouse) constructor.newInstance(new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 這里就可以隨意添加服務(wù)了
        // 根據(jù)不同人的需求,篩選房子
        XiaomingBuyHouse xiaomingBuyHouse = new XiaomingBuyHouse();
        System.out.println("Lianjia幫每個(gè)人篩選好房子");

        // RealSubject,真實(shí)客戶的看房行為
        Object result = method.invoke(xiaomingBuyHouse, args);

        // 有了這個(gè)動(dòng)態(tài)代理的功能,相當(dāng)于能自動(dòng)動(dòng)態(tài)生成符合每個(gè)人需求的獨(dú)立的代理服務(wù)
        // 這樣單個(gè)中介 變成了 中介公司。效率更高、服務(wù)更好了
        return result;
    }
});

lianjia.seekHouse();

// 簡寫
BuyHouse lianjia2 = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new Class[]{ BuyHouse.class }, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 進(jìn)行代理
        return null;
    }
});
lianjia2.seekHouse();

可以看到,java實(shí)現(xiàn)的動(dòng)態(tài)代理離不開這幾個(gè)核心點(diǎn)

1、Proxy.getProxyClass 或者 Proxy.newProxyInstance
2、InvocationHandler#invoke
3、反射

動(dòng)態(tài)代理里,反射是貫穿始終的。

大家可能會奇怪, 一個(gè) ?Proxy? 一個(gè)?InvocationHandler?,底層到底做什么了,怎么就能代理了呢?我們也沒看到調(diào)用 ?InvocationHandler?的關(guān)鍵方法 ?invoke?啊。

源碼分析

下面簡單看源碼分析一下。

java.lang.reflect.Proxy#newProxyInstance
getProxyClass0(loader, intfs)

我們重點(diǎn)關(guān)注參數(shù)里的interfacesinvocationHandle,無論是 Proxy.?newProxyInstance()?方式 還是 ?getProxyClass()?方式,重點(diǎn)都落在了?getProxyClass0(loader, intfs)?

是的,這就是代理類的核心生成邏輯。

proxyClassCache.get(loader, interfaces)
java.lang.reflect.Proxy#getProxyClass0

?
對代理類的緩存策略,后邊就能看出來,這是非常有必要的,這個(gè)緩存數(shù)據(jù)結(jié)構(gòu)相當(dāng)復(fù)雜,我們找到核心的點(diǎn):

java.lang.reflect.WeakCache#get

我們看到proxyClassCache.get(loader, interfaces),無論如何緩存,找return就對了。

if (supplier != null) {
    // supplier might be a Factory or a CacheValue<V> instance
    V value = supplier.get();
    if (value != null) {
        return value;
    }
}

?

supplier.get()

然后就是 ?supplier.get()?,也就是 ?java.lang.reflect.WeakCache.Factory#get?

java.lang.reflect.WeakCache.Factory#get

? 而這里的重點(diǎn)是 value = Objects.requireNonNull(valueFactory.apply(key, parameter));

注:Objects.requireNonNull()返回值 還是參數(shù)本身哈,僅僅是進(jìn)行非空判斷,

public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

所以這里的重點(diǎn)就是 valueFactory.apply(key, parameter);
valueFactory是什么呢?

java.lang.reflect.WeakCache#WeakCache

?

image

?

是的,終于扯到關(guān)鍵了 ?ProxyClassFactory?。

java.lang.reflect.Proxy.ProxyClassFactory#apply
java.lang.reflect.Proxy.ProxyClassFactory#apply

?

到這里基本理順了,所以動(dòng)態(tài)代理的核心,還是利用上面講到的反射等技術(shù),動(dòng)態(tài)生成代理類的過程。

ProxyClassFactory#apply方法里省略里很多邏輯,大家可以展開一下,肯定會似曾相識。

比如:

  1. 在代碼中可以看到JDK生成的代理類的類名是“$Proxy”+序號。
  2. 如果接口是public的,代理類默認(rèn)是public final的,并且生成的代理類默認(rèn)放到com.sun.proxy這個(gè)包下。
  3. 如果接口是非public的,那么代理類也是非public的,并且生成的代理類會放在對應(yīng)接口所在的包下。
  4. 如果接口是非public的,并且這些接口不在同一個(gè)包下,那么就會報(bào)錯(cuò)。
sun.misc.ProxyGenerator.ProxyMethod#generateMethod

如果要繼續(xù)深入追尋 生成的 代理類 和 ?InvocationHandler?的?invoke?的關(guān)系,繼續(xù)往里看兩層,就是了??

sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)

?

sun.misc.ProxyGenerator#generateClassFile

?
sun.misc.ProxyGenerator.ProxyMethod#generateMethod

sun.misc.ProxyGenerator.ProxyMethod#generateMethod

?是的,就是我們生成字節(jié)碼的常用套路。

到這里,就完全扣上了,再回頭看 java實(shí)現(xiàn)的動(dòng)態(tài)代理離不開這幾個(gè)核心點(diǎn)

1、Proxy
2、InvocationHandler
3、反射技術(shù)

參考
https://www.zhihu.com/question/20794107
https://www.cnblogs.com/liuyun1995/p/8157098.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 說到動(dòng)態(tài)代理技術(shù),先回顧代理設(shè)計(jì)模式,動(dòng)態(tài)代理的JAVA實(shí)現(xiàn)只不過采用反射技術(shù)實(shí)現(xiàn)的一種代理設(shè)計(jì)模式。代理...
    明月照我行閱讀 365評論 0 1
  • 一、理解 靜態(tài)代理:靜態(tài)代理是在編譯時(shí)就將接口、實(shí)現(xiàn)類、代理類一股腦兒全部手動(dòng)完成 動(dòng)態(tài)代理:在程序運(yùn)行期間根據(jù)需...
    落撒閱讀 2,486評論 0 3
  • 其他更多java基礎(chǔ)文章:java基礎(chǔ)學(xué)習(xí)(目錄) 經(jīng)過上一節(jié)我們講了Class對象和反射機(jī)制,這節(jié)就來講一下反射...
    Hiwayz閱讀 1,131評論 0 6
  • JDK動(dòng)態(tài)代理的實(shí)現(xiàn)原理 1)通過實(shí)現(xiàn)InvocationHandler接口來自定義自己的InvocationHa...
    tracy_668閱讀 467評論 1 4
  • 過程說明:動(dòng)態(tài)生成目標(biāo)接口的 Class 代理類,這個(gè)代理類是實(shí)現(xiàn)了接口中的所有方法。然后再把此class加載到內(nèi)...
    JerryDai閱讀 309評論 0 0

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