首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

純 Java 實現 AOP:基於 JDK 動態代理與 CGLIB 的核心原理

     編輯:Java 相關知識

AOP 核心概念

面向切面編程(AOP)通過橫切關注點分離業務邏輯與系統級服務(如日誌、事務、權限)。Spring AOP 底層依賴 JDK 動態代理(基於介面)與 CGLIB(基於繼承)兩大技術。

方案一:JDK 動態代理實現

前提條件:目標類必須實現一個或多個介面。

核心組件

// 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); // 拋出異常
    }
}

方案二:CGLIB 代理實現(無需介面)

核心優勢:可代理未實現介面的具體類,通過繼承子類重寫方法實現攔截。

// 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(); // 被攔截
    }
}

進階:支持多切面(Before/After/Around)

通過責任鏈模式組織多個攔截器,實現 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 方法

核心限制提醒

  • JDK 代理僅能代理介面方法,非介面方法無法攔截。
  • CGLIB無法代理 final 類/方法、private 方法。
  • 兩者均為運行時字節碼生成,啟動階段有額外開銷。
  • 純 Java 實現缺乏 Spring 的完整 AOP 生態(切點表達式、切面優先級等)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved