一、介紹
JDK動態代理是代理模式的一種實現方式。因爲它是基於接口來做代理的,所以也常被稱爲接口代理。在JDK動態代理中有兩個重要的角色:
InvocationHandler(Interface)
用戶實現這個接口,來編寫代理類處理的核心邏輯。Proxy(Class)
用來創建一個代理實例,此時需要用到上面自定義的InvocationHandler。
二、功能
動態代理擁有代理模式的基本功能,如:調用真實方法的預處理、模塊化通用功能。除此之外,還可以在運行時動態創建代理對象,無需針對每個接口編寫代理邏輯(針對每個接口都編寫對應的處理邏輯,叫做靜態代理)。
三、使用步驟
- 編寫目標接口、目標實現類
- 自定義InvocationHandler接口實現類,編寫代理處理邏輯
我們來看看InvocationHandler這個接口的唯一一個方法invoke 方法
。
/**
* proxy:代理對象,一般用不到
* method:指代的是我們所要調用真實對象的某個方法的Method對象
* args:指代的是調用真實對象某個方法時接受的參數
**/
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- 創建代理對象
創建代理對象時,需要關聯一個InvocationHandler對象。這樣當我們通過代理對象調用目標方法的時候,這個方法的調用就會被轉發到InvocationHandler這個接口的 invoke 方法中。
我們一般把創建代理對象的方法,直接寫在自定義的InvocationHandler實現類中。
- 使用代理調用目標接口中的方法
四、示例
需求:調用對象的每個方法時,在調用前、調用後、調用異常等都打印出一行日誌。
對於這種需求,我們就需要把打印日誌的功能模塊化起來,不能在每個方法中都編寫這種打印日誌的代碼,那樣會把通用功能和業務功能混合在一起,後續不好維護。
1. 編寫目標接口、目標類
目標接口(因爲JDK動態代理是基於接口實現的,所以被代理的目標類,一定要實現一個接口。)
public interface UserService {
String getUserName(Long userId);
void say(String msg);
}
目標類
public class UserServiceImpl implements UserService {
@Override
public String getUserName(Long userId) {
return "user" + userId;
}
@Override
public void say(String msg) {
System.out.println("say " + msg);
}
}
2. 自定義InvocationHandler
public class LogInvocationHandler implements InvocationHandler {
/**
* 1. 目標類
*/
private final Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
/**
* 2. 代理邏輯
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//調用目標方法
Object result = null;
try {
//前置通知
System.out.println(method.getName() + "方法開始調用...");
result = method.invoke(target, args);
//返回通知, 可以訪問到方法的返回值
System.out.println(method.getName() + "方法返回值:" + result);
} catch (Exception e) {
e.printStackTrace();
//異常通知, 可以訪問到方法出現的異常
System.out.println(method.getName() + "方法調用出現了異常");
}
//後置通知.
System.out.println(method.getName() + "方法調用完成!");
return result;
}
/**
* 3. 獲取目標類代理
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
自定義的InvocationHandler實現類的三部曲(非常重要):
- 重寫invoke方法,編寫代理核心邏輯
- 保存目標對象(就是上面的target對象,在創建代理時會用到)
- 提供創建代理的方法
使用Proxy類
的newProxyInstance方法
實現,需要的三個參數:類加載器、目標類接口、代理邏輯處理類(自定義的InvocationHandler)
這樣使用代理對象調用接口方法時,就可以轉發到代理處理類的invoke方法中了。
3. 使用代理調用目標方法
@Test
public void dynamicProxyTest() {
UserService userService = new UserServiceImpl();
LogInvocationHandler logInvocationHandler = new LogInvocationHandler(userService);
UserService userServiceProxy = (UserService) logInvocationHandler.getProxy();
userServiceProxy.getUserName(1L);
System.out.println("=====================");
userServiceProxy.say("hello");
}
打印結果
可以看到,使用代理對象userServiceProxy來調用接口方法時,請求都轉發到了代理處理類
的invoke方法中,在invoke方法中使用反射調用目標方法,最終轉發到了target目標對象
中。
相關文章:CGLIB動態代理介紹