JDK 動態代理是 Java 面向切面編程 (AOP) 的基石,Spring AOP 默認即基於此實現。本文深入 JDK 源碼 (`java.lang.reflect.Proxy`、`jdk.internal.reflect.ProxyGenerator`),剖析代理類生成、加載、方法分派的完整鏈路。
public interface Service { void execute(); }
public class ServiceImpl implements Service {
public void execute() { System.out.println("真實業務邏輯"); }
}
public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增強: " + method.getName());
Object result = method.invoke(target, args); // 反射調用目標
System.out.println("後置增強");
return result;
}
}
// 客戶端
Service target = new ServiceImpl();
InvocationHandler handler = new MyInvocationHandler(target);
Service proxy = (Service) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 類加載器
target.getClass().getInterfaces(), // 實現的介面數組
handler); // 處理器
proxy.execute();
// java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
Objects.requireNonNull(h);
final Class>[] intfs = interfaces.clone();
// 1. 獲取/生成代理類 Class 對象
Class> cl = getProxyClass0(loader, intfs);
// 2. 獲取構造器 (參數類型: InvocationHandler)
final Constructor> cons = cl.getConstructor(constructorParams);
// 3. 實例化代理對象,注入 handler
return cons.newInstance(new Object[]{h});
}
private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) {
// 1. 校驗介面合法性 (介面、public、無重複、可見性)
// 2. 獲取鍵值 (類加載器 + 介面列表)
// 3. 從緩存獲取或由 ProxyClassFactory 生成
return proxyClassCache.get(loader, interfaces, () ->
ProxyClassFactory.apply(loader, interfaces));
}
// jdk.internal.reflect.ProxyGenerator (內部類)
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
// 1. 生成代理類名: package + "$Proxy" + 數字
String proxyName = proxyPkg + proxyClassNamePrefix + nextUniqueNumber.getAndIncrement();
// 2. 生成字節碼 byte[] (核心難點)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
// 3. 定義類 (native 方法,調用 JVM 類加載器)
try {
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
此類位於 `jdk.internal.reflect`,非公開 API,但可通過 `--add-exports` 訪問或反射調用。其邏輯大致:
public final void execute() throws { // 對應 Service.execute()
try {
// 1. 獲取 Method 對象 (靜態緩存)
Method m = MethodConstants.EXECUTE_METHOD; // 反射查找緩存
// 2. 構建參數數組 (無參則為空數組)
Object[] args = new Object[0];
// 3. 調用 InvocationHandler.invoke
return h.invoke(this, m, args);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e); // 檢查型異常包裝
}
}
// package com.sun.proxy;
public final class $Proxy0 extends Proxy implements Service {
// 靜態 Method 緩存
private static final Method m1; // Object.hashCode
private static final Method m2; // Object.equals
private static final Method m3; // Object.toString
private static final Method m4; // Service.execute
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("hashCode");
m2 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.example.Service").getMethod("execute");
} catch (NoSuchMethodException | ClassNotFoundException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
public $Proxy0(InvocationHandler h) { super(h); }
public final int hashCode() {
try { return ((Integer) h.invoke(this, m1, null)).intValue(); }
catch (Error|RuntimeException e) { throw e; }
catch (Throwable e) { throw new UndeclaredThrowableException(e); }
}
// equals, toString 類似
public final void execute() {
try { h.invoke(this, m4, null); }
catch (Error|RuntimeException e) { throw e; }
catch (Throwable e) { throw new UndeclaredThrowableException(e); }
}
}
| 維度 | JDK Dynamic Proxy | CGLIB | ByteBuddy |
|---|---|---|---|
| 機制 | 介面實現 | 繼承子類 (字節碼操作) | 字節碼生成 (ASM 封裝) |
| 目標要求 | 實現介面 | 非 final 類/方法 | 極其靈活 |
| 性能 (調用) | 反射調用 (較慢) | FastClass 索引 (較快) | 可生成極優化代碼 |
| 依賴 | JDK 內建 | 第三方 (Spring 內嵌) | 第三方 (現代首選) |
| 模塊化友好 | 需 --add-exports | 需 --add-opens | 較好 |
JDK 動態代理通過運行時生成實現目標介面的代理類,將所有方法調用統一分派給 `InvocationHandler.invoke`。核心流程:
理解此機制,有助於排查 AOP 不生效、異常包裝、類加載器隔離、模塊化訪問等疑難雜症,也是掌握 Spring AOP、RPC 框架、中間件原理的基石。