摘要:簡述從這篇文章起,我們將繼續(xù)邂逅設(shè)計(jì)模式系列篇中的第二篇代理模式。代理模式可以說很多初級(jí)中級(jí)開發(fā)者迷惑的設(shè)計(jì)模式。首先我們需要使用類圖直觀地表示出代理模式思想。所以基于代理模式很輕松就實(shí)現(xiàn)。
簡述: 從這篇文章起,我們將繼續(xù)Kotlin邂逅設(shè)計(jì)模式系列篇中的第二篇代理模式。代理模式可以說很多初級(jí)中級(jí)開發(fā)者迷惑的設(shè)計(jì)模式。但是它確實(shí)應(yīng)用很廣,不用多說大家非常熟悉的Retrofit框架,內(nèi)部使用了動(dòng)態(tài)代理設(shè)計(jì)模式,以注解的方式簡化網(wǎng)絡(luò)請(qǐng)求參數(shù)傳遞,從而實(shí)現(xiàn)更高解耦。然而在Kotlin中有天然支持的屬性代理語法特性,可以簡化Java中代理模式實(shí)現(xiàn)的模板代理。
一、介紹代理模式(Proxy Pattern),又稱委托模式,顧名思義就是一個(gè)對(duì)象的實(shí)現(xiàn)委托給另一個(gè)代理對(duì)象來實(shí)現(xiàn)供外部調(diào)用。
二、定義為其他對(duì)象提供一種代理方式來控制對(duì)某個(gè)對(duì)象的訪問,從而更好地保證了該對(duì)象對(duì)外使用的透明性。
三、基本要求1、委托對(duì)象(或者被代理對(duì)象)與代理對(duì)象需要實(shí)現(xiàn)相同的接口。
2、代理對(duì)象中保有實(shí)際的委托對(duì)象引用,外部調(diào)用的操作或行為都是代理對(duì)象在內(nèi)部交于實(shí)際的委托對(duì)象去實(shí)現(xiàn)。
3、為了內(nèi)部隱藏性,外部調(diào)用者直接和兩者共同的接口通信。
三、使用場景當(dāng)無法或不想直接訪問某個(gè)對(duì)象或訪問某個(gè)對(duì)象存在困難時(shí)可以通過一個(gè)代理對(duì)象來間接訪問。代理可以實(shí)現(xiàn)方法增強(qiáng),比如常用的日志,緩存等;也可以實(shí)現(xiàn)方法攔截,通過代理方法修改原方法的參數(shù)和返回值
四、UML類圖代理模式在生活中非常常見,由于最近身邊同事都在討論買房,這里就以買房中介為例來介紹我們今天的代理模式。首先我們需要使用UML類圖直觀地表示出代理模式思想。
由上面的UML的類圖可知,主要涉及到四種角色:
1、Client: 客戶類,可以看做代理模式調(diào)用的外部者
2、IPurchaseHouse: 抽象買房接口,該接口主要職責(zé)是聲明HouseOwner(實(shí)際房子擁有者)與HouseAgent(房產(chǎn)中介)的共同接口方法,該類可以是一個(gè)接口或抽象類
3、HouseOwner: 房子擁有者(房東),也就是代理模式中實(shí)際委托對(duì)象或被代理對(duì)象,外部調(diào)用者Client類就是通過代理對(duì)象(中介)間接調(diào)用實(shí)際的委托對(duì)象中定義的方法
4、HouseAgent: 房產(chǎn)中介,也就是代理模式中的代理對(duì)象,該類持有一個(gè)真實(shí)HouseOwner引用,在代理類中接口方法中調(diào)用HouseOwner方法以此來達(dá)到代理作用。
五、靜態(tài)代理在Java中實(shí)現(xiàn)靜態(tài)代理還是比較簡單,只要按照上述UML中分析角色規(guī)則來定義就能輕松實(shí)現(xiàn)。這里就用Java先去實(shí)現(xiàn)上述例子:
//IPurchaseHouse: 抽象買房接口
interface IPurchaseHouse {
void inquiryPrice();//詢價(jià)
void visitHouse();//看房
void payDeposit();//付定金
void signAgreement();//簽合同
void payMoney();//付錢
void getHouse();//拿房
}
//HouseOwner: 房子擁有者(房東)
class HouseOwner implements IPurchaseHouse {//實(shí)現(xiàn)IPurchaseHouse共同接口
@Override
public void inquiryPrice() {
System.out.println("HouseOwner提出房子價(jià)格: 200W RMB");
}
@Override
public void visitHouse() {
System.out.println("HouseOwner同意買房者來看房子");
}
@Override
public void payDeposit() {
System.out.println("HouseOwner收了買房者1W RMB定金");
}
@Override
public void signAgreement() {
System.out.println("HouseOwner與買房者簽訂合同");
}
@Override
public void payMoney() {
System.out.println("買房者付錢給HouseOwner");
}
@Override
public void getHouse() {
System.out.println("買房者拿到房子");
}
}
//HouseAgent: 房產(chǎn)中介
class HouseAgent implements IPurchaseHouse {
private IPurchaseHouse mHouseOwner;//具體房東HouseOwner被代理對(duì)象引用
public HouseAgent(IPurchaseHouse houseOwner) {
mHouseOwner = houseOwner;
}
@Override
public void inquiryPrice() {
mHouseOwner.inquiryPrice();//通過具體房東HouseOwner引用去調(diào)用inquiryPrice
}
@Override
public void visitHouse() {
mHouseOwner.visitHouse();//通過具體房東HouseOwner引用去調(diào)用visitHouse
}
@Override
public void payDeposit() {
mHouseOwner.payDeposit();//通過具體房東HouseOwner引用去調(diào)用payDeposit
}
@Override
public void signAgreement() {
mHouseOwner.signAgreement();//通過具體房東HouseOwner引用去調(diào)用signAgreement
}
@Override
public void payMoney() {
mHouseOwner.payMoney();//通過具體房東HouseOwner引用去調(diào)用payMoney
}
@Override
public void getHouse() {
mHouseOwner.getHouse();//通過具體房東HouseOwner引用去調(diào)用getHouse
}
}
//Client客戶類
class Client {
public static void main(String[] args) {
IPurchaseHouse houseOwner = new HouseOwner();
IPurchaseHouse houseAgent = new HouseAgent(houseOwner);//傳入具體被代理類實(shí)例
houseAgent.inquiryPrice();//詢問價(jià)格
houseAgent.visitHouse();//看房
houseAgent.payDeposit();//支付定金
houseAgent.signAgreement();//簽合同
houseAgent.payMoney();//付錢
houseAgent.getHouse();//拿房
}
}
運(yùn)行結(jié)果:
HouseOwner提出房子價(jià)格: 200W RMB
HouseOwner同意買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽訂合同
買房者付錢給HouseOwner
買房者拿到房子
Process finished with exit code 0
這就是靜態(tài)代理具體的實(shí)現(xiàn),可能有些并不能看到代理模式所帶來的好處,看上去就像是代理類做了實(shí)際轉(zhuǎn)發(fā)調(diào)用而已。實(shí)際上有個(gè)很明顯優(yōu)點(diǎn)就是: 可以在HouseAgent類中整個(gè)流程插入一些特有的操作或行為,而不會(huì)影響內(nèi)部HouseOwner的實(shí)現(xiàn),保護(hù)內(nèi)部的實(shí)現(xiàn)。 還有一個(gè)優(yōu)點(diǎn)就是代理類在保證HouseOwner核心功能同時(shí)可以擴(kuò)展其他行為。
上述結(jié)論可能有點(diǎn)抽象,假如現(xiàn)在有個(gè)不一樣需求比如A房產(chǎn)中介,在看房之前首先得簽訂一個(gè)看房協(xié)議,但是這個(gè)協(xié)議只涉及購買用戶與中介之間的協(xié)議。所以基于代理模式很輕松就實(shí)現(xiàn)。
//修改后的HouseAgentA
class HouseAgentA implements IPurchaseHouse {
private IPurchaseHouse mHouseOwner;//具體房東HouseOwner被代理對(duì)象引用
private boolean mIsSigned;
public HouseAgentA(IPurchaseHouse houseOwner) {
mHouseOwner = houseOwner;
}
@Override
public void inquiryPrice() {
mHouseOwner.inquiryPrice();//通過具體房東HouseOwner引用去調(diào)用inquiryPrice
}
@Override
public void visitHouse() {
if (mIsSigned) {
System.out.println("您已經(jīng)簽訂了看房協(xié)議,可以看房了");
mHouseOwner.visitHouse();//通過具體房東HouseOwner引用去調(diào)用visitHouse
} else {
System.out.println("很抱歉,您還沒簽訂了看房協(xié)議,暫時(shí)不能看房");
}
}
public void signVisitHouseAgreement(boolean isSigned) {
mIsSigned = isSigned;
}
@Override
public void payDeposit() {
mHouseOwner.payDeposit();//通過具體房東HouseOwner引用去調(diào)用payDeposit
}
@Override
public void signAgreement() {
mHouseOwner.signAgreement();//通過具體房東HouseOwner引用去調(diào)用signAgreement
}
@Override
public void payMoney() {
mHouseOwner.payMoney();//通過具體房東HouseOwner引用去調(diào)用payMoney
}
@Override
public void getHouse() {
mHouseOwner.getHouse();//通過具體房東HouseOwner引用去調(diào)用getHouse
}
}
//Client客戶類
class Client {
public static void main(String[] args) {
IPurchaseHouse houseOwner = new HouseOwner();
IPurchaseHouse houseAgent = new HouseAgentA(houseOwner);//傳入具體被代理類實(shí)例
houseAgent.inquiryPrice();//詢問價(jià)格
((HouseAgentA) houseAgent).signVisitHouseAgreement(true);//簽訂看房合同
houseAgent.visitHouse();//看房
houseAgent.payDeposit();//支付定金
houseAgent.signAgreement();//簽合同
houseAgent.payMoney();//付錢
houseAgent.getHouse();//拿房
}
}
運(yùn)行結(jié)果:
HouseOwner提出房子價(jià)格: 200W RMB
您已經(jīng)簽訂了看房協(xié)議,可以看房了
HouseOwner同意買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽訂合同
買房者付錢給HouseOwner
買房者拿到房子
Process finished with exit code 0
看到了Java中的HouseAgent和HouseAgent中代理類中實(shí)現(xiàn)轉(zhuǎn)發(fā)委托是不是有點(diǎn)無腦啊,有點(diǎn)機(jī)械,就像是在寫Java中的setter和getter方法一樣,太多的樣板代碼。這時(shí)候把它叫給Kotlin吧,它會(huì)讓你的代理類看起來更加簡潔和優(yōu)雅,因?yàn)樵贙otlin中實(shí)現(xiàn)代理模式有著天然優(yōu)勢(shì),熟悉Kotlin的小伙伴們都知道,在Kotlin中有代理獨(dú)有語法特性,通過它就能輕松實(shí)現(xiàn)代理模式。
//IPurchaseHouseKt: 抽象買房接口
interface IPurchaseHouseKt {
fun inquiryPrice() //詢價(jià)
fun visitHouse() //看房
fun payDeposit() //付定金
fun signAgreement() //簽合同
fun payMoney() //付錢
fun getHouse() //拿房
}
//HouseOwnerKt: 房子擁有者(房東)
class HouseOwnerKt : IPurchaseHouseKt {
override fun inquiryPrice() {
println("HouseOwner提出房子價(jià)格: 200W RMB")
}
override fun visitHouse() {
println("HouseOwner同意買房者來看房子")
}
override fun payDeposit() {
println("HouseOwner收了買房者1W RMB定金")
}
override fun signAgreement() {
println("HouseOwner與買房者簽訂合同")
}
override fun payMoney() {
println("買房者付錢給HouseOwner")
}
override fun getHouse() {
println("買房者拿到房子")
}
}
//HouseAgentKt: 房產(chǎn)中介. 注意了,重點(diǎn)來了,Kotlin只需要簡單一行就替代了Java代理類所有樣板代碼
class HouseAgentKt(houseOwnerKt: IPurchaseHouseKt) : IPurchaseHouseKt by houseOwnerKt//通過by關(guān)鍵字實(shí)現(xiàn)代理,省略大量的代理類中的樣板代碼,這一點(diǎn)需要get
//Client調(diào)用處
fun main(args: Array<String>) {
val houseOwnerKt = HouseOwnerKt()
HouseAgentKt(houseOwnerKt).run {
inquiryPrice()//詢問價(jià)格
visitHouse()//看房
payDeposit()//支付定金
signAgreement()//簽合同
payMoney()//付錢
getHouse()//拿房
}
}
運(yùn)行結(jié)果:
HouseOwner提出房子價(jià)格: 200W RMB
HouseOwner同意買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽訂合同
買房者付錢給HouseOwner
買房者拿到房子
Process finished with exit code 0
可能有的小伙伴就會(huì)問了,你使用by關(guān)鍵字一下把所有的方法都給代理了,可是需要像上面新加的需求一樣,需要在某個(gè)方法調(diào)用時(shí)插入一段邏輯。這個(gè)也非常方便,只需要重寫需要改變的那個(gè)方法即可。一起來瞅瞅:
//修改后的HouseAgentAKt
class HouseAgentAKt(houseOwnerAKt: IPurchaseHouseKt) : IPurchaseHouseKt by houseOwnerAKt {
private val mHouseOwnerAKt = houseOwnerAKt
var mIsSigned: Boolean = false
override fun visitHouse() {//只需要重寫visitHouse即可
if (mIsSigned) {
println("您已經(jīng)簽訂了看房協(xié)議,可以看房了")
mHouseOwnerAKt.visitHouse()
} else {
println("很抱歉,您還沒簽訂了看房協(xié)議,暫時(shí)不能看房")
}
}
}
//Client調(diào)用處
fun main(args: Array<String>) {
val houseOwnerKt = HouseOwnerKt()
HouseAgentAKt(houseOwnerKt).run {
mIsSigned = true
inquiryPrice()
visitHouse()
payDeposit()
signAgreement()
payMoney()
getHouse()
}
}
運(yùn)行結(jié)果:
HouseOwner提出房子價(jià)格: 200W RMB
您已經(jīng)簽訂了看房協(xié)議,可以看房了
HouseOwner同意買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽訂合同
買房者付錢給HouseOwner
買房者拿到房子
Process finished with exit code 0
可能就會(huì)有小伙伴問了,在Kotlin中一個(gè)by關(guān)鍵字底層到底做了什么,為什么能減少代理類中樣板代碼。
實(shí)際上,在Kotlin中代理類HouseAgentKt的超類型IPurchaseHouseKt后面的by houseOwnerKt 表示houseOwnerKt將會(huì)在HouseAgentKt中內(nèi)部存儲(chǔ),并且編譯器將自動(dòng)生成委托給houseOwnerKt的所有IPurchaseHouseKt接口方法。
我們可以一起來看下反編譯后的代碼,驗(yàn)證我們的結(jié)論:
public final class HouseAgentKt implements IPurchaseHouseKt {
// $FF: synthetic field
private final IPurchaseHouseKt $$delegate_0;//houseOwnerKt的內(nèi)部存儲(chǔ)$$delegate_0
public HouseAgentKt(@NotNull IPurchaseHouseKt houseOwnerKt) {
Intrinsics.checkParameterIsNotNull(houseOwnerKt, "houseOwnerKt");
super();
this.$$delegate_0 = houseOwnerKt;
}
public void getHouse() {
this.$$delegate_0.getHouse();//委托給$$delegate_0(也即是傳入的houseOwnerKt)getHouse方法
}
public void inquiryPrice() {
this.$$delegate_0.inquiryPrice();//委托給$$delegate_0(也即是傳入的houseOwnerKt)inquiryPrice方法
}
public void payDeposit() {
this.$$delegate_0.payDeposit();//委托給$$delegate_0(也即是傳入的houseOwnerKt)payDeposit方法
}
public void payMoney() {
this.$$delegate_0.payMoney();//委托給$$delegate_0(也即是傳入的houseOwnerKt)payMoney方法
}
public void signAgreement() {
this.$$delegate_0.signAgreement();//委托給$$delegate_0(也即是傳入的houseOwnerKt)signAgreement方法
}
public void visitHouse() {
this.$$delegate_0.visitHouse();//委托給$$delegate_0(也即是傳入的houseOwnerKt)visitHouse方法
}
}
六、動(dòng)態(tài)代理
現(xiàn)在我們需求又增加了,現(xiàn)在需要增加多個(gè)代理中介,可能有很多小伙伴就說再去按照規(guī)則增加幾個(gè)代理類就可以了。盡管Kotlin能解決Java中需要編寫很多樣板代碼的問題,但是始終還是靜態(tài)的。所謂靜態(tài)就是代理者類是需要開發(fā)者自己手動(dòng)編寫,在代碼運(yùn)行前代理類的class編譯文件就已經(jīng)存在。甚至,可能不是編譯前就能決定的代理類的個(gè)數(shù),而是在運(yùn)行時(shí)確定增加代理中介的個(gè)數(shù),面對(duì)這樣場景,靜態(tài)代理可能就無能為力,那么就引出下面的動(dòng)態(tài)代理
在Java中給我們提供了一個(gè)非常方便的動(dòng)態(tài)代理接口InvocationHandler,只要實(shí)現(xiàn)這個(gè)接口然后重寫它的抽象方法invoke()
//DynamicProxy類
class DynamicProxy implements InvocationHandler {
private Object object;//被代理類的引用
DynamicProxy(Object object) {//傳入被代理類的實(shí)例引用
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object, args);
}
}
//Client類
class Client {
public static void main(String[] args) {
IPurchaseHouse houseOwner = new HouseOwner();
DynamicProxy dynamicProxy = new DynamicProxy(houseOwner);
//Proxy.newProxyInstance方法動(dòng)態(tài)構(gòu)造一個(gè)代理中介,需要傳入被代理類的ClassLoader、共同接口集合和dynamicProxy實(shí)例對(duì)象
IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(), new Class[]{IPurchaseHouse.class}, dynamicProxy);
agentA.inquiryPrice();
agentA.visitHouse();
agentA.payDeposit();
agentA.signAgreement();
agentA.payMoney();
agentA.getHouse();
}
}
運(yùn)行結(jié)果:
HouseOwner提出房子價(jià)格: 200W RMB
HouseOwner同意買房者來看房子
HouseOwner收了買房者1W RMB定金
HouseOwner與買房者簽訂合同
買房者付錢給HouseOwner
買房者拿到房子
Process finished with exit code 0
實(shí)際上Java中的動(dòng)態(tài)代理實(shí)現(xiàn)已經(jīng)非常精簡了,所以在Kotlin在動(dòng)態(tài)代理實(shí)現(xiàn)并沒有特別不一樣的,它和Java的實(shí)現(xiàn)沒有不同。所以這里就不再重復(fù)實(shí)現(xiàn),只是換了Kotlin語言實(shí)現(xiàn)沒有什么不一樣的。
七、動(dòng)態(tài)代理原理解析動(dòng)態(tài)代理與靜態(tài)代理不同點(diǎn)在于,它可以動(dòng)態(tài)生成任意個(gè)代理對(duì)象,無需要開發(fā)者手動(dòng)編寫代理類代碼。動(dòng)態(tài)代理機(jī)制在運(yùn)行時(shí)動(dòng)態(tài)生成代理類字節(jié)碼byte數(shù)組,然后通過jvm內(nèi)部將字節(jié)碼byte數(shù)組反序列化對(duì)應(yīng)代理的Class對(duì)象,然后再通過反射機(jī)制創(chuàng)建代理類的實(shí)例。
1、第一步我們先從Proxy.newProxyInstance方法進(jìn)入探究,通過它在外部更為直觀是可以獲取代理類對(duì)象。
class Client {
public static void main(String[] args) {
IPurchaseHouse houseOwner = new HouseOwner();
DynamicProxy dynamicProxy = new DynamicProxy(houseOwner);
//第一步: 從Proxy.newProxyInstance方法入手
IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance(
houseOwner.getClass().getClassLoader(),
new Class[]{IPurchaseHouse.class},
dynamicProxy
);
agentA.inquiryPrice();
agentA.visitHouse();
agentA.payDeposit();
agentA.signAgreement();
agentA.payMoney();
agentA.getHouse();
}
}
2、第二步進(jìn)入Proxy.newProxyInstance方法的定義
Proxy.newProxyInstance有三個(gè)參數(shù):
loader(ClassLoader): 這個(gè)參數(shù)是實(shí)際被代理類的類加載器實(shí)例。
interfaces(Class<);: 代理類和被代理類共同實(shí)現(xiàn)的接口的Class數(shù)組
h(InvocationHandler): 代理攔截器接口,一般需要使用子類去實(shí)現(xiàn)該接口或匿名類去實(shí)現(xiàn)
public static Object newProxyInstance(ClassLoader loader,
Class<);
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<);//將interfaces的Class數(shù)組clone一份副本,賦值給intfs
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {//檢查創(chuàng)建一個(gè)新的代理類需要權(quán)限
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//注意點(diǎn)1: getProxyClass0方法拿到代理類的Class對(duì)象實(shí)例cl
//注意傳入的參數(shù)就是從外部傳入的loader(被代理類的類加載器)、intfs(被代理類實(shí)現(xiàn)所接口的Class[]的副本)
Class<);/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//注意點(diǎn)2: 拿到cl實(shí)例后,就通過反射機(jī)制創(chuàng)建代理類實(shí)例
final Constructor<);//先拿到代理類的構(gòu)造器Constructor實(shí)例cons
final InvocationHandler ih = h;
//檢查代理類構(gòu)造器是否是公有的public權(quán)限, 不是就會(huì)通過AccessController去修改訪問權(quán)限以致于可以創(chuàng)建代理類實(shí)例
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);//將訪問權(quán)限設(shè)置為可訪問的
return null;
}
});
}
//注意點(diǎn)3: 拿到構(gòu)造器實(shí)例cons后,就到了最為關(guān)鍵的也就是最后一步,創(chuàng)建代理類實(shí)例。
//但是需要注意的是構(gòu)造器反射傳入的參數(shù)是h,也就是傳入的InvocationHandler的實(shí)例,也可以進(jìn)一步推論生成的代理類中存在以InvocationHandler為參數(shù)的構(gòu)造器。
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
再一次來梳理下newProxyInstance源碼流程:
首先傳入loader、interfaces、h三個(gè)參數(shù),先將interfacesclone一份副本保存在intfs中,然后檢查創(chuàng)建一個(gè)新的代理類所需要的權(quán)限,接著到了我們第一個(gè)注意點(diǎn)1,就是通過getProxyClass0方法(需要傳入loader和intfs參數(shù))獲得代理類的Class對(duì)象實(shí)例。拿到了代理類實(shí)例后,我們就通過反射的機(jī)制創(chuàng)建代理類實(shí)例;
到了我們的注意點(diǎn)二,通過代理類Class對(duì)象cl獲得構(gòu)造器對(duì)象cons,并檢查構(gòu)造器對(duì)象是否是public,否則就強(qiáng)行修改訪問權(quán)限;
最后到了注意點(diǎn)三,通過cons.newInstance創(chuàng)建代理類對(duì)象,并且構(gòu)造器反射中傳入h(InvocationHandler對(duì)象),說明我們可以推斷一下生成的代理類中存在以InvocationHandler為參數(shù)的構(gòu)造器。
3、第三步進(jìn)入getProxyClass0方法,傳入的參數(shù)loader和intfs,在該方法內(nèi)部會(huì)委托給proxyClassCache的get方法,如果給定的類加載器中定義的代理類實(shí)現(xiàn)了給定的接口,直接返回緩存中的副本,否則它將通過ProxyClassFactory創(chuàng)建代理類.
private static Class<);if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//請(qǐng)注意上面那段英文注釋: 如果給定的類加載器中定義的代理類實(shí)現(xiàn)了給定的接口,
//那么就會(huì)直接返回緩存中的副本,否則它將通過ProxyClassFactory創(chuàng)建代理類
//注意點(diǎn)1: proxyClassCache;注意點(diǎn)2: ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
4、第四步proxyClassCache的介紹和定義, 請(qǐng)注意創(chuàng)建proxyClassCache傳入的構(gòu)造器兩個(gè)參數(shù)分別是: KeyFactory和ProxyClassFactory
/**
* a cache of proxy classes
*/
private static final WeakCachenew WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache是一個(gè)WeakCache
final class WeakCache<K, P, V> {
private final ReferenceQueue refQueue
= new ReferenceQueue<>();
// the key type is Object for supporting null key
private final ConcurrentMap
在WeakCahe類中只有一個(gè)核心get方法,里面包含了整個(gè)緩存的邏輯,注意我們獲取代理類Class對(duì)象,就是通過proxyClassCache.get(loader, interfaces);實(shí)際上就是調(diào)用WeakCache中的get方法.
//K泛型是一級(jí)map的緩存key, P泛型傳入的參數(shù),分別對(duì)應(yīng)外部傳入的 loader和 interfaces
public V get(K key, P parameter) {
...
//通過傳入一級(jí)map的key,通過CacheKey拿到最終
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 懶初始化cacheKey對(duì)應(yīng)的二級(jí)valuesMap, 如果valuesMap為空,就會(huì)新創(chuàng)建一個(gè)空的ConcurrentMap的valueMap,put到一級(jí)緩存map中
ConcurrentMap
我們來一起梳理下WeakCache的邏輯: 首先proxyClassCache就是一個(gè)WeakCache實(shí)例對(duì)象,它有兩個(gè)構(gòu)造器參數(shù)subKeyFactory和valueFactory,創(chuàng)建proxyClassCache實(shí)例對(duì)應(yīng)傳入的是proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory())中的KeyFactory和ProxyClassFactory.
然后在WeakCache內(nèi)部存在二級(jí)ConcurrentHashMap, 一級(jí)map的key就是get方法傳入的key, 通過這個(gè)key拿到cacheKey,從而拿到對(duì)應(yīng)的valuesMap二級(jí)map。
然后又通過根據(jù)傳入的一級(jí)map的key和參數(shù)parameter,subKeyFactory中的apply方法獲得sub-key,通過sub-key拿到二級(jí)map中存儲(chǔ)的Supplier對(duì)象,它可能是一個(gè)CacheValue也有可能是一個(gè)Factory,
最終通過Factory的get方法拿到實(shí)際的值。
對(duì)于上述有兩個(gè)核心注意點(diǎn)
注意點(diǎn)1----->獲取subKey過程: 通過subKeyFactory.apply(key,parameter)拿到sub-key
//weakCache調(diào)用處: Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //KeyFactory的定義 private static final class KeyFactory implements BiFunction
注意點(diǎn)2----> supplier.get()獲取value的過程:
我們都知道supplier對(duì)應(yīng)的可以是Factory對(duì)象,也就是最后會(huì)調(diào)用Factory中的get方法。
@Override
public synchronized V get() { // serialize access
// 再一次檢查supplier,如果傳入從valuesMap拿到的不等于當(dāng)前Factory對(duì)象,因?yàn)樗赡芤呀?jīng)變成CacheValue了,那就直接返回null
Supplier supplier = valuesMap.get(subKey);
if (supplier != this) {
return null;
}
//創(chuàng)建一個(gè)新的value
V value = null;
try {
//注意點(diǎn)出現(xiàn),value最終會(huì)通過valueFactory.apply(key, parameter)拿到
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// 判斷value是否為null
assert value != null;
// 將拿到的value包裝成一個(gè)CacheValue
CacheValue cacheValue = new CacheValue<>(value);
// 嘗試把valuesMap中的對(duì)應(yīng)subKey的Factory替換成cacheValue,
//這就是為什么前面說過valuesMap中取出的Supplier可能是Factory可能是CacheValue,有沒有種偷梁換柱的趕腳
if (valuesMap.replace(subKey, this, cacheValue)) {
//替換成功后,并把cacheValue put到reverseMap中
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
// 成功替換了新的CacheValue,并返回最終的值
return value;
}
通過上述代碼分析,我們知道最終value獲取是來自于valueFactory中apply方法,還記得valueFactory是啥嗎?沒錯(cuò)它就是ProxyClassFactory也就是最終定位到了ProxyClassFactory中的apply方法。這也就是為什么之前說如果緩存中有直接從緩存中返回緩存的副本,沒有就在ProxyClassFactory中創(chuàng)建代理對(duì)象。
5、第五步進(jìn)入ProxyClassFactory中的apply方法進(jìn)行探究,這是創(chuàng)建新的代理類Class對(duì)象唯一來源。
private static final class ProxyClassFactory implements BiFunction
再重新梳理一下ProxyClassFactory中的apply中的邏輯,首先做一些接口驗(yàn)證操作,然后通過ProxyGenerator.generateProxyClass生成確定的代理類Class文件的byte數(shù)組,最后通過defineClass0方法傳入被代理類的類加載器、代理類唯一名稱、生成的代理類文件反序列化成一個(gè)代理類的Class對(duì)象
6、第六步進(jìn)入ProxyGenerator中的generateProxyClass方法進(jìn)行探究,主要通過它來生成代理類Class文件。generateProxyClass方法傳入的參數(shù)主要有: proxyName(唯一代理類名稱), interfaces(需要代理的接口Class數(shù)組), accessFlags(訪問權(quán)限標(biāo)識(shí))
public static byte[] generateProxyClass(final String var0, Class<);int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
//-----注意點(diǎn)----調(diào)用ProxyGenerator中的generateClassFile方法
final byte[] var4 = var3.generateClassFile();
//是否需要把生成Class文件保存在本地文件中,這個(gè)標(biāo)識(shí)可以從外部進(jìn)行配置
//boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace(., File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
7、第七步進(jìn)入generateClassFile()方法,該方法主要生成Class文件。
private byte[] generateClassFile() {
//---注意點(diǎn)1----在生成的代理類中加入Object類幾個(gè)默認(rèn)方法比如常見的hashCode、equal、toString方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
//取出代理類的接口的Class數(shù)組
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//----注意點(diǎn)2---遍歷代理類的接口的Class數(shù)組,將代理類接口中的方法加入生成的代理類中
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
//獲得每個(gè)接口中定義的所有方法Method對(duì)象
Method[] var5 = var4.getMethods();
int var6 = var5.length;
//然后再遍歷所有的Method對(duì)象,并把它加入到生成的代理類中
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
...
try {
//----注意點(diǎn)3 生成的代理類中加入生成構(gòu)造器方法generateConstructor----
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
...
//----注意點(diǎn)4 生成的代理類中加入生成靜態(tài)初始化塊----
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
...
//創(chuàng)建字節(jié)數(shù)組輸出流
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
...
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
//往輸出流寫入生成代理類Filed字段相關(guān)信息
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
//往輸出流寫入生成代理類Method方法相關(guān)信息
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
//返回最終的Class文件的字節(jié)數(shù)組
return var13.toByteArray();
} catch (IOException var9) {
throw
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/7050.html
摘要:是什么著名廠商開發(fā)的基于的靜態(tài)類型編程語言,聲稱。語法近似和,且已活躍在開發(fā)領(lǐng)域,被譽(yù)為平臺(tái)的。各有千秋,我更認(rèn)同改寫字節(jié)碼。的作用是防止敏感字段被泄露到中,的作用是軟刪除數(shù)據(jù)不可見,但沒有真的刪除。 Kotlin是什么? 著名IDE廠商JetBrains開發(fā)的基于JVM的靜態(tài)類型編程語言,聲稱100%?interoperable?with?Java。Kotlin是由工程師設(shè)計(jì)的,各種...
閱讀 713·2023-04-25 19:43
閱讀 3907·2021-11-30 14:52
閱讀 3784·2021-11-30 14:52
閱讀 3852·2021-11-29 11:00
閱讀 3783·2021-11-29 11:00
閱讀 3869·2021-11-29 11:00
閱讀 3557·2021-11-29 11:00
閱讀 6105·2021-11-29 11:00