更新時間:2022-05-13 09:31:35 來源:動力節點 瀏覽747次
這篇文章是關于Java 的動態代理——它是我們在語言中可用的主要代理機制之一。
簡單地說,代理是通過自己的設施(通常是真實方法)傳遞函數調用的前端或包裝器——可能會添加一些功能。
動態代理允許使用一種方法的單個類為具有任意數量方法的任意類的多個方法調用提供服務。動態代理可以被認為是一種外觀,但它可以偽裝成任何接口的實現。在幕后,它將所有方法調用路由到單個處理程序——invoke ()方法。
雖然它不是用于日常編程任務的工具,但動態代理對于框架編寫者來說非常有用。它也可以用于那些直到運行時才知道具體類實現的情況。
此功能內置在標準 JDK 中,因此不需要額外的依賴項。
讓我們構建一個簡單的代理,它實際上不做任何事情,除了打印請求調用的方法并返回一個硬編碼的數字。
首先,我們需要創建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;
}
}
在這里,我們定義了一個簡單的代理,它記錄調用了哪個方法并返回 42。
我們剛剛定義的調用處理程序所服務的代理實例是通過對java.lang.reflect.Proxy類的工廠方法模式調用創建的:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
new DynamicInvocationHandler());
一旦我們有了代理實例,我們就可以正常調用它的接口方法:
proxyInstance.put("hello", "world");
正如預期的那樣,在日志文件中會打印出有關調用put()方法的消息。
由于InvocationHandler是一個函數式接口,因此可以使用 lambda 表達式內聯定義處理程序:
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。
它以完全相同的方式調用:
(int) proxyInstance.get("hello"); // 42
proxyInstance.put("hello", "world"); // exception
讓我們來看看動態代理的一種潛在的真實世界場景。
假設我們想記錄我們的函數執行需要多長時間。為此,我們首先定義一個能夠包裝“真實”對象、跟蹤時間信息和反射調用的處理程序:
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()
在這里,我們代理了一個地圖和一個字符序列(字符串)。
代理方法的調用將委托給被包裝的對象并產生日志語句:
Executing put finished in 19153 ns
Executing get finished in 8891 ns
Executing charAt finished in 11152 ns
Executing length finished in 10087 ns
以上就是關于“什么是動態代理”的介紹,大家如果對此比較感興趣,想了解更多相關知識,可以關注一下動力節點的Java在線學習,里面的課程內容細致全面,很適合沒有基礎的小伙伴學習,希望對大家能夠有所幫助哦。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習