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