代理模式是 Spring AOP 的實現基礎。本文通過安全檢查的業務場景,層層遞進講解靜態代理、JDK 動態代理與 CGLIB 代理的實現原理與代碼實踐。
定義用戶管理接口 UserManager 及實現類 UserManagerImpl。需求:在每個業務方法執行前進行安全檢查,但不想污染業務邏輯代碼。
創建實現同一接口的代理類 UserManagerImplProxy,持有目標對象引用,在代理方法中織入橫切邏輯:
public class UserManagerImplProxy implements UserManager {
private UserManager userManager; // 目標對象引用
public UserManagerImplProxy(UserManager userManager) {
this.userManager = userManager;
}
public void checkSecurity() {
System.out.println("--------安全檢查----------");
}
@Override
public void addUser(String username, String password) {
checkSecurity(); // 橫切邏輯
this.userManager.addUser(username, password); // 委託目標對象
}
// 其他方法同理...
}
缺點:接口方法一多,代理類代碼膨脹,維護成本高,這正是 AOP 自動化代理要解決的問題。
核心是實現 InvocationHandler 接口,利用 Proxy.newProxyInstance 動態生成代理對象:
public class SecurityHandler implements InvocationHandler {
private Object targetObject;
public Object newProxy(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
checkSecurity(); // 統一攔截點
return method.invoke(targetObject, args); // 反射調用目標方法
}
}
優勢:一個處理器類處理所有接口方法,靈活控制是否啟用攔截邏輯。限制:目標類必須實現接口。
針對無接口的類,CGLIB 通過繼承目標類並重寫方法實現代理:
public class CGlibProxyFactory implements MethodInterceptor {
private Object targetObject;
public Object newProxy(Object targetObject) {
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObject.getClass()); // 設置父類
enhancer.setCallback(this); // 設置攔截器
return enhancer.create(); // 生成代理對象
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
checkSecurity();
return method.invoke(targetObject, args);
}
}
關鍵差異:JDK 代理基於接口(組合模式),CGLIB 基於繼承(繼承模式),無法代理 final 類/方法。
| 代理方式 | 實現機制 | 適用場景 | 性能特點 |
|---|---|---|---|
| 靜態代理 | 手工編寫類 | 簡單、固定場景 | 無額外開銷 |
| JDK 動態代理 | 接口+反射 | 目標實現接口 | 調用較快,生成較慢 |
| CGLIB 代理 | 繼承+FastClass | 無接口/補強場景 | 生成較快,調用極快 |
Spring AOP 默認優先使用 JDK 動態代理,目標類無接口時回退到 CGLIB。理解三者原理,有助於排查代理失效、事務不生效等經典問題。