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

Java 動態代理完全指南:JDK 動態代理 vs CGLIB 動態代理深度對比

     編輯:Java 綜合教學

Java 動態代理完全指南:JDK 動態代理 vs CGLIB 動態代理深度對比

動態代理是 Java 進階開發中的核心技術,廣泛應用於 Spring AOP、事務管理、日誌攔截等場景。理解其原理前,先釐清靜態代理:由程式設計師預先編寫代理類,編譯期即確定,一個代理類只能服務一個介面,擴展性極差。

一、靜態代理範例

// 介面
public interface Account {
    void queryAccount();
    void updateAccount();
}

// 目標實現
public class AccountImpl implements Account {
    public void queryAccount() { System.out.println("查看賬戶"); }
    public void updateAccount() { System.out.println("修改賬戶"); }
}

// 靜態代理類
public class AccountProxy implements Account {
    private Account account;
    public AccountProxy(Account account) { this.account = account; }
    public void queryAccount() {
        System.out.println("代理before");
        account.queryAccount();
        System.out.println("代理after");
    }
    public void updateAccount() {
        System.out.println("代理before");
        account.updateAccount();
        System.out.println("代理after");
    }
}

二、JDK 動態代理

核心類:java.lang.reflect.Proxy 與 InvocationHandler。限制:只能代理實現介面的類。

public class AccountProxyFactory implements InvocationHandler {
    private Object target;
    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            this
        );
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
            System.out.println("代理before");
        }
        Object result = method.invoke(target, args);
        if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
            System.out.println("代理after");
        }
        return result;
    }
}

使用方式:Account account = (Account) new AccountProxyFactory().bind(new AccountImpl());

三、CGLIB 動態代理

針對 JDK 只能代理介面的缺陷,CGLIB 通過繼承目標類、覆寫父類方法實現代理,無需實現介面。依賴 asm 與 cglib 套件。

public class AccountCglibProxyFactory implements MethodInterceptor {
    private Object target;
    public Object getInstance(Object target) {
        this.target = target;
        return Enhancer.create(target.getClass(), this);
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
            System.out.println("before");
        }
        Object result = proxy.invokeSuper(obj, args); // 必須用 invokeSuper 避免死循環
        if (!method.getDeclaringClass().getName().equals("java.lang.Object")) {
            System.out.println("after");
        }
        return result;
    }
}

使用方式:Account account = (Account) new AccountCglibProxyFactory().getInstance(new AccountImpl());
甚至可代理無介面的普通類:Person person = (Person) new AccountCglibProxyFactory().getInstance(new Person());

核心差異總結

特性JDK 動態代理CGLIB 動態代理
代理機制介面實現(組合)繼承子類(繼承)
目標類要求必須實現一個或多個介面無需介面,不能為 final 類/方法
效能生成代理快,調用稍慢生成代理較慢(字節碼操作),調用快
適用場景Spring AOP 預設(有介面時)無介面類、第三方庫類增強

最佳實踐建議

  • 優先使用介面設計,配合 JDK 動態代理,符合面向介面編程原則。
  • 無法修改第三方類或類未實現介面時,選用 CGLIB。
  • Spring Boot 2.x 預設 proxy-target-class=true,預設使用 CGLIB,可透過配置調整。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved