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

Java 設計模式精講:靜態代理模式原理、實戰與演進

     編輯:Java 相關知識

代理模式 是結構型設計模式的核心成員,其核心思想是:為目標對象提供一個替身(代理),控制對目標對象的訪問,並在不修改目標代碼的前提下增強功能。本文以「坦克移動」為例,從靜態代理入手,層層遞進展示日誌、計時等橫切關注點的織入,並引出動態代理的必要性。

一、 角色定義與核心契約

角色說明代碼體現
抽象主題定義目標與代理的公共接口interface Moveable { void move(); }
真實主題核心業務邏輯實現class Tank implements Moveable
代理主題持有真實主題引用,實現接口,增強功能class TankTimeProxy implements Moveable

二、 基礎實戰:計時代理

// 1. 抽象契約
public interface Moveable {
    void move();
}

// 2. 真實主題:坦克核心邏輯
public class Tank implements Moveable {
    @Override
    public void move() {
        System.out.println("Tank moving........");
        // 模擬耗時操作
        try { Thread.sleep(100); } catch (InterruptedException ignored) {}
    }
}

// 3. 代理主題:計時增強
public class TankTimeProxy implements Moveable {
    private final Moveable target; // 組合持有目標

    public TankTimeProxy(Moveable target) {
        this.target = target;
    }

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        System.out.println("time1=" + start);
        target.move(); // 委託調用
        long end = System.currentTimeMillis();
        System.out.println("time2=" + end);
        System.out.println("執行耗時: " + (end - start) + "ms");
    }
}

// 4. 客戶端測試
public class TestTank {
    public static void main(String[] args) {
        Tank tank = new Tank();
        Moveable proxy = new TankTimeProxy(tank);
        proxy.move(); // 輸出:time1 → Tank moving → time2 → 耗時
    }
}

三、 疊加增強:日誌代理 + 計時代理 = 鏈式代理

靜態代理最大的優勢在於接口一致性:所有代理都實現 Moveable,因此可以任意組合、嵌套。

// 日誌代理
public class TankLogProxy implements Moveable {
    private final Moveable target;
    public TankLogProxy(Moveable target) { this.target = target; }

    @Override
    public void move() {
        System.out.println(">>> 開始移動 [LOG]");
        target.move();
        System.out.println("<<< 結束移動 [LOG]");
    }
}

// 組合使用:先計時,再日誌(或反過來)
public class TestTank {
    public static void main(String[] args) {
        Tank tank = new Tank();
        Moveable timed = new TankTimeProxy(tank);      // 第1層:計時
        Moveable logged = new TankLogProxy(timed);     // 第2層:日誌
        logged.move();
    }
}
// 輸出順序:
// >>> 開始移動 [LOG]
// time1=...
// Tank moving........
// time2=...
// 執行耗時: ...ms
// <<< 結束移動 [LOG]

這正是 責任鏈/裝飾器 風格的靜態組合,順序靈活可控。

四、 靜態代理的痛點:類爆炸與維護困境

若需為 Tank 添加:事務、權限、緩存、重試、熔斷、監控……每個功能都要手寫一個 XxxProxy 類。

  • 代理類數量 = 目標類數量 × 橫切關注點數量
  • 目標接口新增方法 → 所有代理類必須同步修改
  • 代碼重複度高(委託調用、異常處理樣板代碼)

這正是 動態代理(JDK Proxy、CGLIB、ByteBuddy)誕生的動力:運行時生成代理類,集中處理橫切邏輯。

五、 靜態代理 vs 動態代理 選型指南

維度靜態代理動態代理
代理類生成時間編譯期運行時
目標類要求實現接口JDK: 必須實現接口;CGLIB: 可代理類
性能極高(直接調用)略低(反射/字節碼生成開銷,可忽略)
適用場景固定、少量、性能極度敏感的增強通用 AOP、框架級攔截、批量增強
調試難度低(可直接斷點)中(需查看生成字節碼)

六、 現代最佳實踐:注解驅動 + 動態代理

Spring AOP、Guice AOP、AspectJ 皆是動態代理的工程化封裝。開發者僅需:

@Aspect
@Component
public class TankMonitorAspect {
    @Around("execution(* com.demo.Tank.move(..))")
    public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        log.info(">>> Tank.move 開始");
        Object result = pjp.proceed();
        log.info("<<< Tank.move 結束,耗時 {} ms", (System.nanoTime() - start) / 1e6);
        return result;
    }
}

一個切面替代 N 個靜態代理類,實現業務邏輯與橫切關注點完全解耦。

七、 總結

  1. 靜態代理:手寫代理類,實現同一接口,組合目標對象,調用前後增強邏輯。理解代理本質的最佳入門。
  2. 鏈式組合:多代理嵌套,順序可控,類似裝飾器模式。
  3. 痛點:類爆炸、維護成本高、違背開閉原則。
  4. 演進:動態代理 → AOP 框架 → 注解驅動切面,實現橫切邏輯集中化、標準化、零侵入。

程序員笑話:波音 777 試飛邀請 IBM 技術總監,總監推說「那天老婆過生日」。波音總裁怒:「怯懦鬼,我還沒說日期呢!」

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