更新時(shí)間:2022-10-20 10:10:27 來源:動(dòng)力節(jié)點(diǎn) 瀏覽1702次
Java 動(dòng)態(tài)代理類是原始類之上的一種“附加組件”,它允許Java 開發(fā)人員根據(jù)需要更改原始類的行為。假設(shè),如果您將一個(gè)類用作現(xiàn)成JAR 庫(kù)的一部分,并且您不能簡(jiǎn)單地重寫其源代碼,但您還需要更改該類的行為方式。
例如,您不知道可以在您的對(duì)象上調(diào)用此類的哪個(gè)方法,但您想輸出一個(gè)重要的“確認(rèn)”消息。Java 動(dòng)態(tài)代理是這個(gè)問題的理想解決方案。
在本文中,我們將了解有關(guān) Java 動(dòng)態(tài)代理的所有信息。什么是 Java 動(dòng)態(tài)代理?他們是如何工作的?以及何時(shí)應(yīng)該在您的 Java 代碼中理想地使用它。
Java 動(dòng)態(tài)代理是 Java 代理設(shè)計(jì)模式的一部分。當(dāng)程序需要擴(kuò)展或修改現(xiàn)有類的某些功能時(shí),它允許創(chuàng)建代理對(duì)象。在這種情況下,將實(shí)例化代理對(duì)象而不是原始對(duì)象。通常,代理對(duì)象具有與原始對(duì)象相同的方法,并且在 Java 代理類中擴(kuò)展了原始類。Java 動(dòng)態(tài)代理模式具有原始對(duì)象的句柄,并且也可以調(diào)用原始類方法。
因此,代理類可以以非常方便的方式實(shí)現(xiàn)很多不同的東西,這樣的事情很少有例子包括:
可以將日志記錄方法擴(kuò)展到原始類以記錄方法何時(shí)啟動(dòng)和停止。
可以使用 Java 動(dòng)態(tài)代理對(duì)參數(shù)執(zhí)行額外的檢查。
代理類也可用于在最終確定之前模擬原始類的行為,以檢查它是否按預(yù)期執(zhí)行。
如果不修改類的原始代碼,就無(wú)法完成上述應(yīng)用,這使得它們成為 Java 動(dòng)態(tài)代理的理想應(yīng)用。
在這樣的應(yīng)用程序中,代理類不直接在原始類對(duì)象上實(shí)現(xiàn)功能。遵循單一職責(zé)原則,代理類只創(chuàng)建一個(gè)代理,并在處理程序中修改實(shí)際行為。當(dāng)調(diào)用代理對(duì)象而不是原始對(duì)象時(shí),Java 動(dòng)態(tài)代理將決定是否必須調(diào)用原始方法或處理程序。處理程序可以執(zhí)行其擴(kuò)展任務(wù),也可以調(diào)用原始方法來執(zhí)行原始任務(wù)。
它是 Java 編程語(yǔ)言中一個(gè)相對(duì)高級(jí)的主題,因?yàn)樗枰恍V泛的 Java 知識(shí)。計(jì)劃使用 Java 動(dòng)態(tài)代理的開發(fā)人員必須了解反射類的使用或字節(jié)碼操作或如何編譯動(dòng)態(tài)生成的 Java 代碼。要?jiǎng)?chuàng)建字節(jié)碼,首先應(yīng)該學(xué)習(xí)如何使用 cglib 或 bytebuddy 或內(nèi)置的 Java 編譯器。
InvocationHandler 是一個(gè)特殊的接口,它允許我們攔截對(duì)對(duì)象的任何方法調(diào)用并添加我們需要的附加行為。我們首先需要通過創(chuàng)建一個(gè)實(shí)現(xiàn)此接口的類來創(chuàng)建我們的攔截器。它只包含一個(gè)方法:invoke(),其中原始對(duì)象作為參數(shù)傳遞,被代理。
當(dāng)我們考慮代理類和它們調(diào)用的處理程序時(shí),就會(huì)明白為什么責(zé)任分離在這里非常重要。代理類總是在運(yùn)行時(shí)生成,但代理類調(diào)用的處理程序可以用源代碼編碼,并且可以在編譯時(shí)與整個(gè)程序的代碼一起編譯。
代理類及其實(shí)例是使用 java.lang.reflect.Proxy 類的靜態(tài)方法創(chuàng)建的。它是 JDK 的一部分,可以創(chuàng)建代理類或直接創(chuàng)建它的實(shí)例。Java 內(nèi)置代理的使用相對(duì)容易一些。您需要做的就是實(shí)現(xiàn)一個(gè) java.lang.InvocationHandler,以便代理對(duì)象稍后可以調(diào)用它。在此之后,調(diào)用來自 Invocation Handler 的 invoke() 方法。
另一個(gè)方法,Proxy.getProxyClass 方法返回一個(gè)代理類的 java.lang.Class 對(duì)象,給定一個(gè)類加載器和一個(gè)接口數(shù)組。
根據(jù)動(dòng)態(tài)代理類 Java 文檔,必須傳遞給 Proxy.getProxyClass 的參數(shù)有一些限制:
interfaces 數(shù)組中的所有 Class 對(duì)象都必須表示接口,而不是類或原始類型。
interfaces 數(shù)組中的兩個(gè)元素不能引用相同的 Class 對(duì)象。
所有接口類型必須通過指定的類加載器按名稱可見。
所有非公共接口必須在同一個(gè)包中;否則,代理類將不可能實(shí)現(xiàn)所有接口,無(wú)論它定義在什么包中。
對(duì)于具有相同簽名的指定接口的任意數(shù)量的成員方法:
如果任何方法的返回類型是 void 或原始類型,則所有方法都必須具有相同的返回類型。
如果不是,則其中一個(gè)方法必須具有可以分配給所有方法的所有返回類型的返回類型。
生成的代理類不得超過虛擬機(jī)對(duì)類施加的任何限制。
下面的代碼演示了在代碼中使用 Java 動(dòng)態(tài)代理的所有討論點(diǎn):
包代理;
導(dǎo)入java.lang.reflect.InvocationHandler;
導(dǎo)入java.lang.reflect.InvocationTargetException;
導(dǎo)入java.lang.reflect.Method;
導(dǎo)入java.lang.reflect.Proxy;
公共類 ProxyDemo {
接口如果{
void originalMethod(String str);
}
靜態(tài)類原始實(shí)現(xiàn)If {
public void originalMethod(String str) {
System.out.println(str);
}
}
靜態(tài)類Handler實(shí)現(xiàn)InvocationHandler {
private final If original;
public Handler(如果是原始的) {
this.original = 原創(chuàng);
}
public Object invoke(Object proxy, Method method, Object[] args)
拋出 IllegalAccessException、IllegalArgumentException、
調(diào)用目標(biāo)異常{
System.out.println("代理前:");
method.invoke(原始, args);
System.out.println("代理后:");
返回空值;
}
}
public static void main(String[] args){
原始原始=新原始();
處理程序處理程序=新處理程序(原始);
If a = (If) Proxy.newProxyInstance(If.class.getClassLoader(),
新類[] { If.class },
處理程序);
a.originalMethod("你好");
}
}
現(xiàn)在,如果處理程序想要調(diào)用原始對(duì)象的原始方法,首先,它必須有權(quán)訪問它。這不會(huì)由 Java 代理實(shí)現(xiàn)提供。開發(fā)人員必須在代碼中手動(dòng)將此參數(shù)傳遞給處理程序?qū)嵗?/p>
您一定已經(jīng)注意到,通常將一個(gè)名為“proxy”的對(duì)象作為參數(shù)傳遞給調(diào)用處理程序。這是由 Java 反射動(dòng)態(tài)生成的代理對(duì)象,而不是我們要代理的對(duì)象。因此,開發(fā)人員為每個(gè)原始類使用單獨(dú)的處理程序?qū)ο?,或者他們也可以使用一些共享?duì)象,如果有任何方法可以調(diào)用,他們也可以知道要調(diào)用哪個(gè)原始對(duì)象。您還可以創(chuàng)建一個(gè)調(diào)用處理程序和一個(gè)沒有任何原始對(duì)象的接口的代理。您不需要任何類來實(shí)現(xiàn)代碼中的接口。動(dòng)態(tài)創(chuàng)建的代理類會(huì)自動(dòng)實(shí)現(xiàn)接口。
Java 動(dòng)態(tài)代理在流行技術(shù)中被積極使用。在安全框架中可以看到一種常見的用法。
它用于在授權(quán)人員應(yīng)該使用的所有方法中添加安全檢查。使用 Java 動(dòng)態(tài)代理,可以添加安全檢查,導(dǎo)致用戶輸入有效憑據(jù),而無(wú)需復(fù)制驗(yàn)證碼來訪問每個(gè)方法。
它還可以用于為方法創(chuàng)建日志。這也很容易通過使用代理類來實(shí)現(xiàn)。您可以簡(jiǎn)單地向原始方法添加額外的代碼,以在最后顯示完整的日志。
以上就是關(guān)于“Java動(dòng)態(tài)代理的簡(jiǎn)介”,大家如果想了解更多相關(guān)知識(shí),不妨來關(guān)注一下動(dòng)力節(jié)點(diǎn)Java視頻教程,里面的課程內(nèi)容從入門到精通,細(xì)致全面,通俗易懂,很適合沒有基礎(chǔ)的小伙伴學(xué)習(xí),希望對(duì)大家能夠有所幫助。
相關(guān)閱讀
Java實(shí)驗(yàn)班
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
Java就業(yè)班
有基礎(chǔ) 直達(dá)就業(yè)
Java夜校直播班
業(yè)余時(shí)間 高薪轉(zhuǎn)行
Java在職加薪班
工作1~3年,加薪神器
Java架構(gòu)師班
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)