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

Java 設計模式精講:代理模式(靜態/動態/CGLIB)核心原理與實戰

     編輯:Java 綜合教學

Java 設計模式精講:代理模式(靜態/動態/CGLIB)核心原理與實戰

代理模式為目標對象提供代理,控制訪問並可增強功能。分為靜態代理(手寫)、JDK 動態代理(介面)、CGLIB 動態代理(繼承類)。本文解析三種形式核心原理、代碼實現、優缺點與 Spring AOP 選型邏輯。

核心角色

  • Subject(抽象主題):定義真實主題與代理共同介面。
  • RealSubject(真實主題):核心業務邏輯實現。
  • Proxy(代理):持有 RealSubject 引用,轉發調用並可前後增強。

1. 靜態代理:手寫代理類

// 介面
public interface UserDao { void save(); void delete(); }

// 真實對象
public class UserDaoImpl implements UserDao {
    public void save() { System.out.println("保存數據..."); }
    public void delete() { System.out.println("刪除數據..."); }
}

// 代理對象(同包/目錄)
public class UserDaoProxy implements UserDao {
    private UserDao target;
    public UserDaoProxy(UserDao target) { this.target = target; }
    public void save() {
        System.out.println("開啟事務");
        target.save();
        System.out.println("提交事務");
    }
    public void delete() {
        System.out.println("開啟事務");
        target.delete();
        System.out.println("提交事務");
    }
}

缺點:每個目標類需手寫一個代理類,代碼冗餘極大,維護成本高。

2. JDK 動態代理:基於介面

使用:UserDao proxy = (UserDao) JdkProxyFactory.createProxy(new UserDaoImpl());
限制:目標類**必須實現介面**,代理通過介面多態調用。

3. CGLIB 動態代理:基於繼承

使用:UserDao proxy = (UserDao) CglibProxyFactory.createProxy(UserDaoImpl.class);
原理:生成目標類子類,重寫非 final 方法。
限制:無法代理 `final` 類/方法、`private` 方法;需引入 `cglib` 依賴。

三種代理對照

維度靜態代理JDK 動態代理CGLIB 動態代理
目標類要求任意必須實現介面非 final 類、非 final 方法
代理生成編譯期手寫運行期反射生成介面實現類運行期字節碼生成子類
性能(JDK8+)最快優化後接近直接調用FastClass 機制,略快於早期 JDK
適用場景極少數固定增強標準介面編程、Spring AOP 默認無介面類、第三方庫、性能敏感

Spring AOP 代理選型邏輯

// 簡化版 ProxyFactory 邏輯
if (targetClass.isInterface() || proxyTargetClass == false) {
    return new JdkDynamicAopProxy(config);  // 默認優先 JDK
} else {
    return new ObjenesisCglibAopProxy(config);  // 強制/無介面用 CGLIB
}
  • Spring Boot 2.x+ 默認 `spring.aop.proxy-target-class=false`(優先 JDK)。
  • 設置 `spring.aop.proxy-target-class=true` 強制 CGLIB。
  • 目標類無介面 → 自動降級 CGLIB。

實戰最佳實踐

  1. 面向介面編程:Service 定義介面,優先 JDK 代理,解耦實現。
  2. 避免 final 方法:Service 方法非 final,保證 CGLIB 可代理。
  3. 事務/日誌/權限/緩存:典型橫切關注點,交給 AOP 代理織入。
  4. 性能敏感熱點:考慮靜態代理或手寫切面,減少反射/字節碼開銷。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved