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

Java 理論與實踐:動態代理實現修飾器、攔截器與遠程代理模式

     編輯:Java 相關知識

動態代理:設計模式的輕量級實現

動態代理為 Facade、Bridge、Interceptor、Decorator、Proxy (遠程/虛擬)、Adapter 等模式提供了運行時生成代理類的替代方案。雖然傳統靜態類也能實現,但動態代理更簡潔、彈性,能消除大量手寫或代碼生成的 Stub 類。

Proxy 模式回顧

核心思想:建立「存根」或「替身」物件,接收請求並轉發給真實執行工作的物件。

  • RMI:讓遠端 JVM 物件如同本地物件般呼叫。
  • EJB:代理添加遠程調用、安全檢查、事務邊界管理。
  • JAX-RPC / Web Services:讓遠端服務呈現為本地介面。

呼叫端持有介面引用,無法區分是代理還是真實物件。代理負責:查找真實物件、參數編碼/解碼 (Marshalling/Unmarshalling)、網路傳輸、異常轉換。

從靜態 Stub 到動態代理的演進

JDK 5.0 之前

  • RMI 使用 rmic 編譯期生成 _Stub / _Skel 類。
  • JAX-RPC 工具生成 Web Service 代理類。
  • 缺點:增加編譯步驟、類別爆炸、命名混淆。

JDK 5.0+

  • RMI 內建改用動態代理,無需 rmic,開發體驗大幅提升。
  • 多數 J2EE 容器 (WebLogic, JBoss 等) 以動態代理實現 EJB 容器服務 (安全、事務、攔截)。
  • 動態代理提供「介面上所有方法調用的集中控制路徑」,完美契合攔截器模式。

動態代理核心機制

核心介面:java.lang.reflect.InvocationHandler

public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
  • 每個代理實例關聯一個 InvocationHandler。
  • 代理方法被調用時,JVM 將呼叫轉發給 invoke(),參數包含:代理物件本身、被調用 Method 物件、實際參數陣列。
  • 最簡實作:return method.invoke(target, args); (委派給目標物件)。

關鍵限制

  • 代理只能實作 介面,不能繼承類 (單繼承限制)。
  • 代理類無名稱、無建構子,必須由 Proxy.newProxyInstance() 工廠方法建立。

典型應用場景

  1. 聲明式事務/安全:invoke() 中開啟/提交事務、檢查權限。
  2. 效能監控/日誌:記錄方法進出、耗時、參數。
  3. 延遲載入:首次調用時才初始化昂貴資源 (Hibernate Proxy)。
  4. 遠程調用封裝:RMI、Hessian、gRPC 客戶端 Stub。

程式碼範例:簡易攔截器

public class LoggingHandler implements InvocationHandler {
    private final Object target;
    public LoggingHandler(Object target) { this.target = target; }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.nanoTime();
        try {
            return method.invoke(target, args);
        } finally {
            System.out.printf("[%s] %s 執行耗時: %d ms%n",
                new Date(), method.getName(), (System.nanoTime() - start) / 1_000_000);
        }
    }
}

// 使用方式
MyInterface target = new MyImpl();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new LoggingHandler(target));
proxy.doSomething(); // 自動記錄日誌

總結

動態代理是 Java 反射機制的皇冠上的明珠,將「橫切關注點」與「業務邏輯」乾淨分離,是 AOP (面向切面編程) 的基石。Spring AOP、Hibernate Lazy Loading、RMI 等核心框架均大量依賴它。

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