代理模式是 AOP、事務管理、遠端呼叫等框架的基石。Java 提供 JDK 動態代理與 CGLIB 兩大主流實現。本文從靜態代理痛點切入,深度解析動態代理生成原理、InvocationHandler 回調機制、CGLIB 繼承式代理差異,並附帶原始碼級反編譯分析。
// 目標介面
interface Hosee { String sayhi(); }
// 目標實現
class HoseeImpl implements Hosee {
public String sayhi() { return "Welcome"; }
}
// 代理類(每個目標類需寫一個代理類)
class HoseeProxy implements Hosee {
private Hosee target;
public HoseeProxy(Hosee target) { this.target = target; }
public String sayhi() {
System.out.println("I'm proxy!");
return target.sayhi();
}
}
弊端:目標類增加 → 代理類成倍增加,維護成本極高。核心原因:代理關係在編譯期固定。
class MyInvocationHandler 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("Before");
Object result = method.invoke(target, args);
System.out.println("After");
return result;
}
}
設定 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true") 後反編譯 $Proxy0.class:
public final class $Proxy0 extends Proxy implements HoseeDynamic {
private static Method m1; // equals
private static Method m3; // sayhi
private static Method m0; // hashCode
private static Method m2; // toString
public $Proxy0(InvocationHandler h) { super(h); }
public final String sayhi() {
try {
return (String) this.h.invoke(this, m3, null); // 全委派給 InvocationHandler
} catch (Throwable t) { throw new UndeclaredThrowableException(t); }
}
// equals/hashCode/toString 同理委派
static {
// 靜態區塊快取 Method 物件
m3 = Class.forName("HoseeDynamic").getMethod("sayhi", new Class[0]);
}
}
關鍵發現:
Proxy、實現目標介面InvocationHandler.invoke()Proxy.newProxyInstance() 內部流程:驗證 → 優化 → 快取 → 同步 → 生成位元碼(ProxyGenerator.generateProxyClass()) → 定義類。位元碼生成是核心,運行期才確定代理行為。
final 類與 final 方法
class CGlibHosee {
public String sayhi() { return "Welcome"; }
}
class CGlibProxy implements MethodInterceptor {
public Object bind(Object 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("Before");
Object result = proxy.invokeSuper(obj, args); // 呼叫父類方法
System.out.println("After");
return result;
}
}
| 方式 | 效能 | 說明 |
|---|---|---|
| proxy.invokeSuper() | 高 | FastClass 機制,直接索引調用父類方法 |
| method.invoke(target, args) | 低 | 標準反射,需存取檢查、參數封裝 |
| 維度 | JDK 動態代理 | CGLIB |
|---|---|---|
| 代理類型 | 介面代理 | 類繼承代理 |
| 目標類要求 | 實現介面 | 非 final 類 |
| 生成機制 | 位元碼生成實現介面 | ASM 生成子類覆寫方法 |
| 效能 | 調用較慢(反射) | 調用較快 |
| Spring 默認策略 | 有介面用 JDK,無介面用 CGLIB |
final 方法/類,或改用組合模式MethodProxy.invokeSuper 或字節碼框架掌握動態代理原理,不僅能深度理解 Spring AOP、RPC 框架,更能在需要無侵入式擴展時靈活選型。