在不修改目標類別原始碼前提下,攔截方法呼叫,織入橫切關注點(事務、日誌、權限、快取、監控)。Spring AOP 底層即基於動態代理。
InvocationHandler:單一方法 invoke(proxy, method, args),攔截所有代理方法呼叫。Proxy:靜態工廠 newProxyInstance(loader, interfaces, handler) 產生代理實例。只能代理介面。目標類別必須實作至少一個介面。
// 目標介面
public interface UserService {
void addUser();
void deleteUser();
}
// 目標實作
public class UserServiceImpl implements UserService {
public void addUser() { System.out.println("add user"); }
public void deleteUser() { System.out.println("delete user"); }
}
// 代理處理器
public class TransactionInterceptor implements InvocationHandler {
private final Object target;
public TransactionInterceptor(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>> 開啟事務");
try {
Object result = method.invoke(target, args);
System.out.println(">>> 提交事務");
return result;
} catch (Exception e) {
System.out.println(">>> 回滾事務");
throw e;
}
}
}
// 客戶端
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TransactionInterceptor(target));
proxy.addUser();
// 輸出:
// >>> 開啟事務
// add user
// >>> 提交事務
MethodInterceptor 攔截,MethodProxy.invokeSuper() 呼叫父類別邏輯。<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class TransactionInterceptorCglib implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(">>> [CGLIB] 開啟事務");
try {
Object result = proxy.invokeSuper(obj, args); // 呼叫父類別方法
System.out.println(">>> [CGLIB] 提交事務");
return result;
} catch (Exception e) {
System.out.println(">>> [CGLIB] 回滾事務");
throw e;
}
}
}
// 客戶端
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class); // 目標類別
enhancer.setCallback(new TransactionInterceptorCglib());
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
proxy.addUser();
| 維度 | JDK Proxy | CGLIB |
|---|---|---|
| 代理類型 | 介面代理 | 類別繼承代理 |
| 目標限制 | 需實作介面 | 不能為 final 類別/方法 |
| 生成機制 | 執行期生成實作介面的匿名類 | 執行期生成子類別 (ASM 操作 bytecode) |
| 方法攔截 | 所有介面方法 | 所有非 final 公開/保護方法 |
| 效能 (歷史) | 早期較慢 (反射調用) | 早期較快 (FastClass 索引) |
| 效能 (現代 JDK 8+) | invokeDynamic 優化,差距極小 | 仍略快,但生成類較多佔 Metaspace |
| Spring Boot 預設 | spring.aop.proxy-target-class=false | true (預設強制 CGLIB) |
@EnableAspectJAutoProxy(proxyTargetClass = true) // 強制 CGLIB
@EnableAspectJAutoProxy(proxyTargetClass = false) // 強制 JDK (需介面)
final,除非確定不需 AOP。@CompileStatic (Groovy) 或手寫代理類別。