代理模式:為目標對象提供一個代理對象,控制對真實對象的訪問。核心價值在於在不修改目標類代碼的前提下,增強/攔截方法調用。典型應用場景:
代理類在編譯期就已存在(.class 文件),直接實現同一接口,持有真實對象引用。
// 接口
public interface IDBQuery { String request(); }
// 真實對象(構造耗時 10s 模擬重資源初始化)
public class DBQuery implements IDBQuery {
public DBQuery() { try { Thread.sleep(10_000); } catch (Exception e) {} }
public String request() { return "real data"; }
}
// 靜態代理:延遲初始化真實對象
public class DBQueryProxy implements IDBQuery {
private DBQuery real;
public String request() {
if (real == null) real = new DBQuery(); // 首次調用才創建
return real.request();
}
}
// 客戶端
IDBQuery proxy = new DBQueryProxy();
System.out.println(proxy.request()); // 首次調用耗時 10s,之後極快
缺點:每個接口需手寫代理類,接口變更雙方同步修改,維護成本高。
public class JdkProxyHandler implements InvocationHandler {
private IDBQuery target;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target == null) target = new DBQuery(); // 延遲初始化
System.out.println("JDK Proxy: before " + method.getName());
Object result = method.invoke(target, args);
System.out.println("JDK Proxy: after");
return result;
}
public static IDBQuery createProxy() {
return (IDBQuery) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{IDBQuery.class},
new JdkProxyHandler()
);
}
}
機制:Proxy.newProxyInstance 生成實現目標接口的匿名類,將所有方法調用轉發給 InvocationHandler.invoke。限制:目標類必須實現接口。
// 依賴: cglib:cglib-nodep:3.3.0
public class CglibProxyFactory 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");
return result;
}
}
特點:無需接口,通過繼承目標類生成子類代理。無法代理 final 類/方法。
API 風格更接近 Java 源碼編程,性能介於 JDK 與 CGLIB 之間,現較少直接使用,多被框架內部集成。
| 維度 | JDK Proxy | CGLIB | Javassist |
|---|---|---|---|
| 代理類生成速度 | 最快 (native defineClass) | 中等 | 較慢 |
| 方法調用性能 | 較慢 (反射調用) | 最快 (FastClass 索引直調) | 中等 |
| 目標類要求 | 必須實現接口 | 非 final 類/方法 | 無特殊限制 |
| 適用場景 | 接口導向編程、AOP 標準 | 無接口類、高性能調用 | 框架底層、複雜字節碼改寫 |
結論:實際業務中方法調用頻率遠高於代理創建頻率,CGLIB 調用性能優勢明顯;但 Spring Boot 2.x+ 默認 AOP 仍優先 JDK Proxy(接口規範),無接口時自動降級 CGLIB。
Enhancer.create() 觸發 DefaultGeneratorStrategy.generate() 生成代理類字節碼 (byte[])。ReflectUtils.defineClass() 反射調用 ClassLoader.defineClass() 將字節碼載入 JVM。ReflectUtils.newInstance() 反射實例化代理類並返回。全過程無磁盤 I/O,純內存操作,毫秒級完成。
invoke/intercept 中做耗時邏輯,影響被代理方法響應。this 指向問題:代理內部調用自身方法不會觸發攔截器,需注入代理自身引用或使用 AOP 框架暴露代理 (AopContext.currentProxy())。代理模式是框架級開發的基石。掌握「靜態代理 → JDK 動態代理 → CGLIB/Javassist」演進脈絡,理解字節碼生成、類加載、方法分派機制,能讓開發者在設計擴展點、排查 AOP 失效、優化 RPC 框架時遊刃有餘。