更新時(shí)間:2022-05-13 09:31:35 來源:動力節(jié)點(diǎn) 瀏覽737次
這篇文章是關(guān)于Java 的動態(tài)代理——它是我們在語言中可用的主要代理機(jī)制之一。
簡單地說,代理是通過自己的設(shè)施(通常是真實(shí)方法)傳遞函數(shù)調(diào)用的前端或包裝器——可能會添加一些功能。
動態(tài)代理允許使用一種方法的單個類為具有任意數(shù)量方法的任意類的多個方法調(diào)用提供服務(wù)。動態(tài)代理可以被認(rèn)為是一種外觀,但它可以偽裝成任何接口的實(shí)現(xiàn)。在幕后,它將所有方法調(diào)用路由到單個處理程序——invoke ()方法。
雖然它不是用于日常編程任務(wù)的工具,但動態(tài)代理對于框架編寫者來說非常有用。它也可以用于那些直到運(yùn)行時(shí)才知道具體類實(shí)現(xiàn)的情況。
此功能內(nèi)置在標(biāo)準(zhǔn) JDK 中,因此不需要額外的依賴項(xiàng)。
讓我們構(gòu)建一個簡單的代理,它實(shí)際上不做任何事情,除了打印請求調(diào)用的方法并返回一個硬編碼的數(shù)字。
首先,我們需要創(chuàng)建java.lang.reflect.InvocationHandler的子類型:
public class DynamicInvocationHandler implements InvocationHandler {
private static Logger LOGGER = LoggerFactory.getLogger(
DynamicInvocationHandler.class);
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
LOGGER.info("Invoked method: {}", method.getName());
return 42;
}
}
在這里,我們定義了一個簡單的代理,它記錄調(diào)用了哪個方法并返回 42。
我們剛剛定義的調(diào)用處理程序所服務(wù)的代理實(shí)例是通過對java.lang.reflect.Proxy類的工廠方法模式調(diào)用創(chuàng)建的:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
new DynamicInvocationHandler());
一旦我們有了代理實(shí)例,我們就可以正常調(diào)用它的接口方法:
proxyInstance.put("hello", "world");
正如預(yù)期的那樣,在日志文件中會打印出有關(guān)調(diào)用put()方法的消息。
由于InvocationHandler是一個函數(shù)式接口,因此可以使用 lambda 表達(dá)式內(nèi)聯(lián)定義處理程序:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
(proxy, method, methodArgs) -> {
if (method.getName().equals("get")) {
return 42;
} else {
throw new UnsupportedOperationException(
"Unsupported method: " + method.getName());
}
});
在這里,我們定義了一個處理程序,它為所有 get 操作返回 42,并為其他所有操作拋出UnsupportedOperationException。
它以完全相同的方式調(diào)用:
(int) proxyInstance.get("hello"); // 42
proxyInstance.put("hello", "world"); // exception
讓我們來看看動態(tài)代理的一種潛在的真實(shí)世界場景。
假設(shè)我們想記錄我們的函數(shù)執(zhí)行需要多長時(shí)間。為此,我們首先定義一個能夠包裝“真實(shí)”對象、跟蹤時(shí)間信息和反射調(diào)用的處理程序:
public class TimingDynamicInvocationHandler implements InvocationHandler {
private static Logger LOGGER = LoggerFactory.getLogger(
TimingDynamicInvocationHandler.class);
private final Map<String, Method> methods = new HashMap<>();
private Object target;
public TimingDynamicInvocationHandler(Object target) {
this.target = target;
for(Method method: target.getClass().getDeclaredMethods()) {
this.methods.put(method.getName(), method);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.nanoTime();
Object result = methods.get(method.getName()).invoke(target, args);
long elapsed = System.nanoTime() - start;
LOGGER.info("Executing {} finished in {} ns", method.getName(),
elapsed);
return result;
}
}
隨后,此代理可用于各種對象類型:
Map mapProxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(), new Class[] { Map.class },
new TimingDynamicInvocationHandler(new HashMap<>()));
mapProxyInstance.put("hello", "world");
CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { CharSequence.class },
new TimingDynamicInvocationHandler("Hello World"));
csProxyInstance.length()
在這里,我們代理了一個地圖和一個字符序列(字符串)。
代理方法的調(diào)用將委托給被包裝的對象并產(chǎn)生日志語句:
Executing put finished in 19153 ns
Executing get finished in 8891 ns
Executing charAt finished in 11152 ns
Executing length finished in 10087 ns
以上就是關(guān)于“什么是動態(tài)代理”的介紹,大家如果對此比較感興趣,想了解更多相關(guān)知識,可以關(guān)注一下動力節(jié)點(diǎn)的Java在線學(xué)習(xí),里面的課程內(nèi)容細(xì)致全面,很適合沒有基礎(chǔ)的小伙伴學(xué)習(xí),希望對大家能夠有所幫助哦。
初級 202925
初級 203221
初級 202629
初級 203743