在軟體設計中,代理模式 是一種結構型模式,核心思想是:「為目標對象提供一個替身 (代理),控制對目標的存取,並在不修改目標程式碼的前提下增強功能」。生活中的例子比比皆是:房仲代理買房、經紀人代理藝人商演、票務代理售票。核心價值在於 關注點分離:目標專注核心業務,代理處理横切關注點 (日誌、事務、權限、快取、延遲載入等)。
// 1. 抽象角色
public interface Business {
void doAction();
}
// 2. 真實角色
public class BusinessImpl implements Business {
@Override
public void doAction() {
System.out.println("執行核心業務邏輯...");
}
}
// 3. 代理角色
public class BusinessProxy implements Business {
private Business realSubject;
public BusinessProxy(Business realSubject) {
this.realSubject = realSubject;
}
@Override
public void doAction() {
// 前置增強
doBefore();
// 委派真實對象
realSubject.doAction();
// 後置增強
doAfter();
}
private void doBefore() { System.out.println("[Proxy] 前置處理:記錄日誌、檢查權限..."); }
private void doAfter() { System.out.println("[Proxy] 後置處理:釋放資源、統計耗時..."); }
}
// 4. 客戶端測試
public class Client {
public static void main(String[] args) {
Business real = new BusinessImpl();
Business proxy = new BusinessProxy(real); // 依賴注入真實對象
proxy.doAction();
}
}
JDK 動態代理利用 反射機制,在運行時根據指定介面動態生成匿名代理類位元組碼,解決了「類別爆炸」問題。核心類:`java.lang.reflect.Proxy`、`java.lang.reflect.InvocationHandler`。
只能代理實作了介面的類,無法代理普通類 (非介面) 或 final 類/方法。這是因為動態生成的類繼承自 `Proxy`,Java 不支援多重繼承。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 定義業務介面 (必須是介面)
public interface BusinessFoo { void foo(); }
public interface BusinessBar { String bar(String msg); }
// 2. 真實實作
public class BusinessFooImpl implements BusinessFoo {
@Override public void foo() { System.out.println("BusinessFooImpl.foo()"); }
}
public class BusinessBarImpl implements BusinessBar {
@Override public String bar(String msg) {
System.out.println("BusinessBarImpl.bar()");
return msg;
}
}
// 3. 通用調用處理器 (InvocationHandler) - 核心邏輯集中在此
public class DynamicProxyHandler implements InvocationHandler {
private final Object target; // 持有真實對象引用
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 統一前置邏輯 (AOP 切面)
System.out.println("[DynamicProxy] Before: " + method.getName());
// 2. 反射調用真實方法
Object result = method.invoke(target, args);
// 3. 統一後置邏輯
System.out.println("[DynamicProxy] After: " + method.getName());
return result;
}
// 工廠方法:生成代理實例
public static Object newProxyInstance(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 類加載器
target.getClass().getInterfaces(), // 實作的介面陣列
new DynamicProxyHandler(target) // 處理器
);
}
}
// 4. 客戶端測試
public class DynamicProxyDemo {
public static void main(String[] args) {
// 為 Foo 建立代理
BusinessFoo fooProxy = (BusinessFoo) DynamicProxyHandler.newProxyInstance(new BusinessFooImpl());
fooProxy.foo();
System.out.println("---");
// 為 Bar 建立代理 (同一 Handler 復用)
BusinessBar barProxy = (BusinessBar) DynamicProxyHandler.newProxyInstance(new BusinessBarImpl());
String ret = barProxy.bar("Hello Dynamic Proxy");
System.out.println("Return: " + ret);
}
}
JDK 動態代理要求目標實作介面。CGLIB (Code Generation Library) 通過繼承目標類並覆寫非 final 方法,實現對 普通類 的代理。Spring AOP 預設策略:有介面用 JDK,無介面用 CGLIB (可強制 `proxyTargetClass=true`)。
// CGLIB 範例 (需引入 cglib 依賴)
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass()); // 繼承目標類
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("[CGLIB] Before: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 呼叫父類 (目標) 方法
System.out.println("[CGLIB] After: " + method.getName());
return result;
}
}
| 維度 | 靜態代理 | JDK 動態代理 | CGLIB 動態代理 |
|---|---|---|---|
| 目標類要求 | 實作介面 | 必須實作介面 | 普通類 (非 final) |
| 生成方式 | 編譯期手寫 | 運行時反射生成位元組碼 | 運行時繼承生成子類位元組碼 |
| 性能 (JDK 8+) | 最快 (直接調用) | 較快 (invoke 優化後接近直接調用) | 較快 (FastClass 機制索引調用) |
| 方法限制 | 無 | 無法代理 final/private/static 方法 | 無法代理 final/private/static 方法 |
| 適用場景 | 固定、少量目標 | Spring AOP 預設、介面導向設計 | 無介面類、第三方庫類增強 |
Spring AOP 正是建立在動態代理之上:
理解動態代理原理,有助於排查 Spring AOP 失效場景 (如:內部方法呼叫繞過代理、final 方法無法增強、非 Spring Bean 無法代理)。
代理模式是 AOP、RPC、ORM、快取等框架的基石。靜態代理 適合簡單場景;JDK 動態代理 是介面導向設計的標準選擇,效能優異;CGLIB 彌補了無介面類的代理缺口。掌握 `InvocationHandler.invoke` 與 `MethodInterceptor.intercept` 的編寫技巧,即掌握了框架級擴展的核心鑰匙。