上篇介紹的靜態代理雖能分離關注點,但面臨「接口變更波及面廣」「代理類爆炸」等痛點。動態代理利用 Java 反射機制,在運行期動態生成代理類,完美解決「一個業務類對應一個代理類」的擴展性難題。
實現動態代理需掌握兩大核心類:
java.lang.reflect.Proxy:提供靜態方法 newProxyInstance() 生成代理對象。java.lang.reflect.InvocationHandler:代理實例的調用處理器,所有方法調用均委派給其 invoke() 方法。沿用上篇的 UserManager 接口與 UserManagerImpl 實現,構建通用日誌處理器 LogHandler。
public class LogHandler implements InvocationHandler {
private Object targetObject; // 目標對象
/** 綁定目標對象並生成代理實例 */
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this
);
}
/** 統一攔截所有方法調用 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始執行方法-->>" + method.getName());
// 可在此處添加參數日誌、性能監控、事務控制等橫切邏輯
Object result = method.invoke(targetObject, args); // 調用真實方法
System.out.println("方法執行結束-->>" + method.getName());
return result;
}
}
public class Client {
public static void main(String[] args) {
LogHandler logHandler = new LogHandler();
// 動態生成代理對象,無需額外定義代理類
UserManager userManager = (UserManager) logHandler.newProxyInstance(new UserManagerImpl());
userManager.addUser("0001", "鐘躍民");
String name = userManager.findUser("0001");
System.out.println("Client.main() --- " + name);
}
}
| 維度 | 靜態代理 | 動態代理 |
|---|---|---|
| 代理類定義 | 編譯期確定,需手寫 | 運行期動態生成 |
| 方法攔截粒度 | 逐方法轉發,代碼冗餘 | 統一 invoke() 集中處理 |
| 接口變更影響 | 所有代理類需同步修改 | 無需修改代理邏輯 |
| 適用場景 | 固定、少量業務接口 | 大量接口、通用橫切關注點(AOP) |
動態代理將接口所有方法調用集中到 InvocationHandler.invoke(),實現了「橫切邏輯」與「業務邏輯」的徹底解耦。這正是 Spring AOP(面向切面編程)的底層基石,也是 SSH 等經典框架廣泛應用的核心機制。掌握動態代理,是深入理解主流框架原理的關鍵一步。