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

Java 動態代理深度解析:JDK Proxy 與 CGLIB 實現原理與 AOP 應用實戰

     編輯:Java 相關知識

動態代理核心價值

動態代理在運行時動態生成代理類,實現對目標對象方法調用的攔截與增強,是 AOP(面向切面編程)的核心實現機制。Spring、MyBatis、Dubbo 等主流框架廣泛應用。

兩大實現方式對比

維度JDK 動態代理CGLIB
核心原理接口代理:生成實現目標接口的匿名類繼承代理:生成目標類的子類,重寫方法
前置條件目標類必須實現一個或多個接口目標類不能為 final、方法不能為 final
核心類java.lang.reflect.Proxy、InvocationHandlernet.sf.cglib.proxy.Enhancer、MethodInterceptor
性能特點調用通過反射,創建快、調用稍慢FastClass 機制索引調用,創建慢、調用快
適用場景有接口、輕量級代理無接口、高性能調用、第三方類增強

JDK 動態代理實戰:事務攔截器

定義業務接口

public interface UserService {
    void addUser();
    void removeUser();
    void searchUser();
}

實現類

public class UserServiceImpl implements UserService {
    public void addUser() { System.out.println("add user"); }
    public void removeUser() { System.out.println("remove user"); }
    public void searchUser() { System.out.println("search user"); }
}

實現 InvocationHandler

public class TransactionInterceptor implements InvocationHandler {
    private Object target;  // 目標對象
    
    public void setTarget(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;
        }
    }
}

創建代理對象

public class JdkProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTarget(target);
        
        // 關鍵:Proxy.newProxyInstance
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),      // 類加載器
            target.getClass().getInterfaces(),       // 實現的接口
            interceptor                              // 處理器
        );
        
        proxy.addUser();  // 通過代理調用
    }
}

運行輸出

【事務】開始事務
add user
【事務】提交事務

CGLIB 動態代理實戰

實現 MethodInterceptor

public class UserServiceCglibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("【CGLIB事務】開始事務");
        Object result = proxy.invokeSuper(obj, args);  // 調用父類方法
        System.out.println("【CGLIB事務】提交事務");
        return result;
    }
}

創建增強對象

public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);  // 目標類
        enhancer.setCallback(new UserServiceCglibInterceptor());
        
        UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
        proxy.addUser();
    }
}

運行輸出

【CGLIB事務】開始事務
add user
【CGLIB事務】提交事務

Spring AOP 代理選擇策略

// Spring 默認策略
@EnableAspectJAutoProxy(proxyTargetClass = false)  // 默認 JDK 代理(有接口)
@EnableAspectJAutoProxy(proxyTargetClass = true)   // 強制 CGLIB 代理

判斷邏輯:

  1. 目標類實現接口 → 默認 JDK 代理
  2. 目標類無接口 或 proxyTargetClass=true → CGLIB 代理
  3. Spring Boot 2.0+ 默認 proxyTargetClass=true(優先 CGLIB)

核心原理深度剖析

JDK Proxy 生成代碼結構

public final class $Proxy0 extends Proxy implements UserService {
    protected $Proxy0(InvocationHandler h) { super(h); }
    
    public void addUser() {
        try {
            super.h.invoke(this, M_addUser, new Object[0]);
        } catch (Error|RuntimeException e) { throw e; }
        catch (Throwable e) { throw new UndeclaredThrowableException(e); }
    }
    // ... 其他接口方法
}

特點:繼承 Proxy、實現目標接口、方法體委託 InvocationHandler.invoke

CGLIB 生成代碼結構

public class UserServiceImpl$$EnhancerByCGLIB$$123456 extends UserServiceImpl {
    private MethodInterceptor CGLIB$CALLBACK_0;
    
    public void addUser() {
        MethodInterceptor interceptor = CGLIB$CALLBACK_0;
        if (interceptor != null) {
            interceptor.intercept(this, M_addUser, new Object[0], P_addUser);
        } else {
            super.addUser();
        }
    }
}

特點:繼承目標類、重寫非 final 方法、委託 MethodInterceptor.intercept

常見坑點與最佳實踐

問題原因解決
final 方法不生效CGLIB 無法重寫 final 方法避免 final 方法、或使用 JDK 代理
私有方法不攔截代理無法訪問 private 方法改為 protected/package-private
內部方法調用失效this.method() 繞過代理AopContext.currentProxy() 或重構
序列化失敗動態生成類無 serialVersionUID實現 Serializable、設置 serialVersionUID
性能問題頻繁創建代理/反射調用開銷緩存代理對象、使用 CGLIB FastClass

應用場景擴展

  • Spring 事務管理:@Transactional 底層即動態代理
  • MyBatis Mapper 代理:接口無實現類,JDK 代理生成 SQL 執行邏輯
  • Dubbo 服務引用:遠程調用代理、負載均衡、熔斷
  • 緩存切面:@Cacheable 方法結果緩存
  • 日誌/監控/權限切面:橫切關注點統一處理

總結

動態代理是 Java 框架開發的基石技術。JDK Proxy 基於接口、標準庫無依賴;CGLIB 基於繼承、功能更強、性能更優。理解兩者原理、適用場景、限制與 Spring 整合策略,對掌握 AOP 本質、排查代理相關疑難雜症、設計擴展性框架具有關鍵價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved