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

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

     編輯:Java 綜合教學

Java 設計模式精講:代理模式

代理模式為物件提供一個替身,控制對真實物件的存取,並在不修改目標代碼前提下增強功能。本文詳細對比靜態代理、JDK 動態代理、CGLIB 動態代理三種實現方式的原理、代碼結構、優缺點與適用場景。

一、模式角色與核心思想

  • 抽象主題:定義真實主題與代理共同介面。
  • 真實主題:實作核心業務邏輯。
  • 代理主題:持有真實主題引用,實作相同介面,可在調用前後織入橫切邏輯(驗證、日誌、事務、緩存等)。

二、靜態代理

代碼結構

// 1. 共同介面
public interface TicketManager {
    void soldTicket();
    void changeTicket();
    void returnTicket();
}

// 2. 真實主題
public class TicketManagerImpl implements TicketManager {
    public void soldTicket() { System.out.println("售票"); }
    public void changeTicket() { System.out.println("改簽"); }
    public void returnTicket() { System.out.println("退票"); }
}

// 3. 代理類(編譯期確定)
public class StaticProxyTicketManager implements TicketManager {
    private TicketManager target;
    public StaticProxyTicketManager(TicketManager target) { this.target = target; }
    public void soldTicket() {
        checkIdentity();          // 前置增強
        target.soldTicket();      // 委派真實主題
        log();                    // 後置增強
    }
    // changeTicket/returnTicket 同理
    private void checkIdentity() { System.out.println("身份驗證"); }
    private void log() { System.out.println("日誌..."); }
}

// 4. 客戶端
public class Client {
    public static void main(String[] args) {
        TicketManager tm = new StaticProxyTicketManager(new TicketManagerImpl());
        tm.soldTicket();
    }
}

優缺點

優點缺點
結構簡單、易理解、不改動目標類每個目標類需一個代理類 → 類爆炸;代碼冗余;編譯期靜態綁定,彈性差

三、JDK 動態代理

核心機制

  • 運行時透過 java.lang.reflect.Proxy 生成實作目標介面的匿名類。
  • 代理類必須實作 InvocationHandler,所有方法調用轉發至 invoke(proxy, method, args)。
  • 前提:目標類必須實作至少一個介面。

代碼實現

public class JdkDynamicProxy implements InvocationHandler {
    private Object target;
    public Object newProxyInstance(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 {
        checkIdentity();                 // 前置
        Object result = method.invoke(target, args); // 反射調用真實方法
        log();                           // 後置
        return result;
    }
    private void checkIdentity() { System.out.println("身份驗證"); }
    private void log() { System.out.println("日誌..."); }
}

// 客戶端
TicketManager tm = (TicketManager) new JdkDynamicProxy()
    .newProxyInstance(new TicketManagerImpl());
tm.soldTicket();

優缺點

優點缺點
單一代理類服務多介面;運行時動態生成;解耦目標與增強邏輯僅能代理介面;反射調用效率略低;代碼可讀性稍弱

四、CGLIB 動態代理

核心機制

  • 底層使用 ASM 字節碼操作框架,生成目標類的子類,覆寫父類非 final 方法。
  • 無需介面,直接代理類;但 final 類/方法不可代理。
  • 核心類:Enhancer(增強器)、MethodInterceptor(攔截器)、MethodProxy(高效反射調用)。

代碼實現

public class CglibProxy implements MethodInterceptor {
    private Object target;
    public Object getInstance(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 {
        checkIdentity();                     // 前置
        Object result = proxy.invokeSuper(obj, args); // 快速調用父類方法
        log();                               // 後置
        return result;
    }
    private void checkIdentity() { System.out.println("身份驗證"); }
    private void log() { System.out.println("日誌..."); }
}

// 客戶端
TicketManager tm = (TicketManager) new CglibProxy()
    .getInstance(new TicketManagerImpl());
tm.soldTicket();

優缺點

優點缺點
無需介面;生成類後調用效率高(FastClass 機制);Spring AOP 預設選擇無法代理 final 類/方法;繼承機制限制;生成類開銷較大(可緩存緩解)

五、三種代理對比總結

維度靜態代理JDK 動態代理CGLIB 動態代理
代理目標具體類實作介面的類任意非 final 類
生成時機編譯期運行時運行時
核心技術手寫類反射 + ProxyASM 字節碼 + 繼承
效能最高(直接調用)中等(反射)高(FastClass,生成後)
適用場景固定少量目標、教學演示介面導向設計、標準化攔截無介面類、高效能 AOP、Spring 預設

六、實戰選型建議

  1. 目標類實作介面 → 優先 JDK 動態代理(標準、輕量)。
  2. 目標類無介面或需代理具體類方法 → CGLIB。
  3. Spring AOP:proxy-target-class=false(預設 JDK)、true(強制 CGLIB)。
  4. 極簡場景/教學 → 靜態代理最直觀。

靜態代理類圖 JDK 動態代理類圖

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