代理模式是常見的設計模式,代理類與委託類實現相同介面,代理類負責預處理、過濾、轉發請求及後續處理。依建立時機分為靜態代理與動態代理。
由程式設計師建立或工具生成代碼,編譯期即存在 .class 檔案。缺點:一個代理類只能服務一個介面,易產生大量重複代碼。
Count 介面:定義 queryCount()、updateCount()CountImpl:委託類,實作業務邏輯CountProxy:代理類,實作 Count,持有 CountImpl 參考,方法中添加事務處理邏輯TestCount:測試類,建立代理並呼叫方法運行時透過反射機制動態生成代理類,核心為 java.lang.reflect.Proxy 與 InvocationHandler 介面。
invoke(Object proxy, Method method, Object[] args) 定義代理邏輯(ClassLoader loader, Class[] interfaces, InvocationHandler h) 動態生成代理物件public class BookFacadeProxy implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("事務開始");
Object result = method.invoke(target, args);
System.out.println("事務結束");
return result;
}
}
限制:目標類必須實作介面,無法代理未實作介面的類。
針對類而非介面,原理是生成目標類的子類並覆寫方法實現增強。無法代理 final 修飾的類與方法。
intercept(Object obj, Method method, Object[] args, MethodProxy proxy)public class BookFacadeCglib implements MethodInterceptor {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.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("事務開始");
proxy.invokeSuper(obj, args); // 呼叫父類方法
System.out.println("事務結束");
return null;
}
}
| 特性 | JDK 動態代理 | CGLIB 動態代理 |
|---|---|---|
| 代理目標 | 介面 | 類(生成子類) |
| 核心機制 | 反射 + Proxy | 字節碼處理 + 繼承 |
| final 限制 | 無 | 無法代理 final 類/方法 |
| 效能 | 生成快、調用稍慢 | 生成慢、調用快 |
| 適用場景 | 目標實作介面 | 目標未實作介面、需高效能調用 |
Spring AOP 預設:介面用 JDK,類用 CGLIB(可透過 proxy-target-class="true" 強制 CGLIB)。