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

Java 動態代理核心原理:Proxy 與 InvocationHandler 深度解析

     編輯:Java 綜合教學

動態代理:運行時生成代理類,攔截方法調用實現 AOP 核心能力

Java 動態代理是 Spring AOP、RPC 框架、事務管理等核心基礎設施的基石。JDK 提供 `java.lang.reflect.Proxy` 與 `InvocationHandler` 兩大核心類,實現基於介面的代理。

核心類職責

  • InvocationHandler:攔截器介面,唯一方法 `invoke(proxy, method, args)`。所有代理方法調用均委派給此方法處理(前置邏輯、調用目標、後置邏輯、返回結果)。
  • Proxy:工廠類,靜態方法 `newProxyInstance(loader, interfaces, handler)` 動態生成實現指定介面的代理類實例,並綁定 `InvocationHandler`。

類圖關係

動態代理類圖

  • `Subject`:業務介面,定義契約。
  • `RealSubject`:真實實現類。
  • `MyInvocationHandler`:實現 `InvocationHandler`,持有 `RealSubject` 引用,在 `invoke` 中織入增強邏輯。
  • `$Proxy0`:運行時生成的代理類,實現 `Subject`,內部持有 `InvocationHandler`。

完整代碼實現

// 1. 業務介面
public interface Subject {
    void say(String str);
}

// 2. 真實實現
public class SubjectBean implements Subject {
    @Override public void say(String str) { System.out.println(str); }
}

// 3. 自定義 InvocationHandler
public class MyInvocationHandler implements InvocationHandler {
    private final Subject target;
    public MyInvocationHandler(Subject target) { this.target = target; }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("pre");           // 前置增強
        Object result = method.invoke(target, args); // 調用目標方法
        System.out.println("post");          // 後置增強
        return result;
    }
}

// 4. 代理工廠方法
public class ProxyFactory {
    public static Subject createProxy(Subject target) {
        return (Subject) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new MyInvocationHandler(target)
        );
    }
}

// 5. 客戶端調用
public class Main {
    public static void main(String[] args) {
        Subject target = new SubjectBean();
        Subject proxy = ProxyFactory.createProxy(target);
        proxy.say("hello"); // 輸出: pre -> hello -> post
    }
}

運行時調試:代理類長什麼樣?

代理類結構

  • 類名:`com.sun.proxy.$Proxy0`(`$Proxy` + 數字編號)。
  • 實現了目標介面(`Subject`),繼承 `Proxy`。
  • 構造器接收 `InvocationHandler`,所有方法調用轉發給 `handler.invoke()`。

核心流程總結

  1. 調用 `Proxy.newProxyInstance`,JVM 動態生成代理類字節碼並載入。
  2. 代理實例持有 `InvocationHandler` 引用。
  3. 客戶端調用代理方法 → 代理類生成的方法體內調用 `handler.invoke(proxy, method, args)`。
  4. `InvocationHandler.invoke` 內部可執行任意邏輯,最後通過 `method.invoke(target, args)` 調用真實對象。

局限與補充

  • 僅能代理介面:JDK 動態代理要求目標類實現至少一個介面。類代理需用 CGLIB/ByteBuddy(繼承子類重寫方法)。
  • 性能:反射調用有開銷,高頻熱點路徑可考慮字節碼生成(ASM)或 MethodHandle 優化。
  • Spring AOP:默認 JDK 動態代理,目標類無介面時自動降級 CGLIB,可配置 `proxy-target-class=true` 強制 CGLIB。

掌握動態代理原理,是理解現代 Java 框架「無侵入式」增強能力的鑰匙。

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