先來理一下概念理論
代理三要素
抽象主題角色(Subject)
具體主題角色(RealSubject)
代理主題角色(Proxy)
代理關(guān)系圖

栗子
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提供的Proxy和InvocationHandler結(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?啊。
源碼分析
下面簡單看源碼分析一下。
getProxyClass0(loader, intfs)
我們重點(diǎn)關(guān)注參數(shù)里的interfaces和invocationHandle,無論是 Proxy.?newProxyInstance()?方式 還是 ?getProxyClass()?方式,重點(diǎn)都落在了?getProxyClass0(loader, intfs)?
是的,這就是代理類的核心生成邏輯。
proxyClassCache.get(loader, interfaces)
?
對代理類的緩存策略,后邊就能看出來,這是非常有必要的,這個(gè)緩存數(shù)據(jù)結(jié)構(gòu)相當(dāng)復(fù)雜,我們找到核心的點(diǎn):
我們看到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?
? 而這里的重點(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是什么呢?
?
?
是的,終于扯到關(guān)鍵了 ?ProxyClassFactory?。
java.lang.reflect.Proxy.ProxyClassFactory#apply
?
到這里基本理順了,所以動(dòng)態(tài)代理的核心,還是利用上面講到的反射等技術(shù),動(dòng)態(tài)生成代理類的過程。
ProxyClassFactory#apply方法里省略里很多邏輯,大家可以展開一下,肯定會似曾相識。
比如:
- 在代碼中可以看到JDK生成的代理類的類名是“$Proxy”+序號。
- 如果接口是public的,代理類默認(rèn)是public final的,并且生成的代理類默認(rèn)放到com.sun.proxy這個(gè)包下。
- 如果接口是非public的,那么代理類也是非public的,并且生成的代理類會放在對應(yīng)接口所在的包下。
- 如果接口是非public的,并且這些接口不在同一個(gè)包下,那么就會報(bào)錯(cuò)。
sun.misc.ProxyGenerator.ProxyMethod#generateMethod
如果要繼續(xù)深入追尋 生成的 代理類 和 ?InvocationHandler?的?invoke?的關(guān)系,繼續(xù)往里看兩層,就是了??
?
?
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