面向切面編程(AOP)通過橫切關注點分離業務邏輯與系統級服務(如日誌、事務、權限)。Spring AOP 底層依賴 JDK 動態代理(基於介面)與 CGLIB(基於繼承)兩大技術。
前提條件:目標類必須實現一個或多個介面。
// 1. 業務介面
public interface Calculator {
int calculate(int a, int b);
}
// 2. 目標實現
public class CalculatorImpl implements Calculator {
public int calculate(int a, int b) { return a / b; }
}
// 3. 切面處理器(實現 InvocationHandler)
public class ValidationHandler implements InvocationHandler {
private Object target;
public ValidationHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增強:參數校驗
if ((int)args[1] == 0) throw new IllegalArgumentException("除數不能為零");
System.out.println("[前置] 執行 " + method.getName() + " 參數: " + Arrays.toString(args));
Object result = method.invoke(target, args); // 執行目標方法
// 後置增強
System.out.println("[後置] 結果: " + result);
return result;
}
}
// 4. 代理工廠
public class ProxyFactory {
public static Object getProxy(Class> intf, Object target, InvocationHandler h) {
return Proxy.newProxyInstance(
intf.getClassLoader(),
new Class[]{intf},
h
);
}
}
// 5. 測試驗證
public class Test {
public static void main(String[] args) {
Calculator target = new CalculatorImpl();
Calculator proxy = (Calculator) ProxyFactory.getProxy(
Calculator.class, target, new ValidationHandler(target)
);
System.out.println("結果: " + proxy.calculate(20, 10));
// proxy.calculate(5, 0); // 拋出異常
}
}
核心優勢:可代理未實現介面的具體類,通過繼承子類重寫方法實現攔截。
// 1. 目標類(無介面)
public class ClientBean {
private String name;
public void addClient() { System.out.println("新增客戶: " + name); }
public String getName() { return name; }
public void setName(String n) { name = n; }
}
// 2. CGLIB 攔截器
public class CGLibProxy implements MethodInterceptor {
private Object target;
public Object createProxy(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 {
// 權限檢查邏輯
if (target instanceof ClientBean && ((ClientBean)target).getName() != null) {
return proxy.invokeSuper(obj, args); // 調用父類方法
}
System.out.println("權限不足,攔截操作");
return null;
}
}
// 3. 測試
public class Test {
public static void main(String[] args) {
CGLibProxy p = new CGLibProxy();
ClientBean bean = (ClientBean) p.createProxy(new ClientBean("Admin"));
bean.addClient(); // 正常執行
bean = (ClientBean) p.createProxy(new ClientBean());
bean.addClient(); // 被攔截
}
}
通過責任鏈模式組織多個攔截器,實現 Spring 風格的切面鏈:
public class AspectChain {
private List interceptors = new ArrayList<>();
private int index = 0;
public void addInterceptor(Interceptor i) { interceptors.add(i); }
public Object proceed(Object target, Method method, Object[] args) throws Throwable {
if (index >= interceptors.size())
return method.invoke(target, args);
Interceptor current = interceptors.get(index++);
return current.intercept(this, target, method, args);
}
}
// 使用示例
AspectChain chain = new AspectChain();
chain.addInterceptor(new BeforeLoggingInterceptor());
chain.addInterceptor(new TransactionInterceptor());
chain.addInterceptor(new AfterLoggingInterceptor());
Object result = chain.proceed(target, method, args);
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 目標實現介面 | JDK 動態代理 | JDK 原生、無第三方依賴、效能穩定 |
| 目標為具體類、無介面 | CGLIB | 通過繼承實現代理、支援 final 方法限制較少 |
| 高效能、低延遲要求 | JDK (Java 8+) | JDK 8 優化了 MethodHandle,效能接近 CGLIB |
| 需要代理 final 方法 | CGLIB (需配置) | JDK 無法代理 final 方法 |