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

JDK 動態代理機制全流程圖解:從 InvocationHandler 到 $Proxy0 誕生

     編輯:Java 程式設計入門

動態代理三大核心組件

  1. 目標對象:實現業務邏輯的真實類 (UserDAOImpl)。
  2. 介面:代理與目標共同實現的契約 (UserDAO),代理實現此介面實現多態。
  3. InvocationHandler:代理邏輯集中處理器,攔截所有方法調用。

代碼結構速覽


// 1. 介面
interface UserDAO { void save(User u); void delete(); }

// 2. 目標實現
class UserDAOImpl implements UserDAO { 
    public void save(User u) { System.out.println("user saved!"); }
    public void delete() { System.out.println("user deleted"); }
}

// 3. 通用攔截器
class LogInterceptor implements InvocationHandler {
    private Object target; // 持有目標引用
    public void setTarget(Object target) { this.target = target; }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + " start"); // 前置增強
        Object result = method.invoke(target, args);      // 調用目標方法
        System.out.println(method.getName() + " end");    // 後置增強
        return result;
    }
}

// 4. 客戶端創建代理
UserDAO target = new UserDAOImpl();
LogInterceptor handler = new LogInterceptor();
handler.setTarget(target);
UserDAO proxy = (UserDAO) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),   // 類加載器
    target.getClass().getInterfaces(),    // 實現的介面列表
    handler                               // 處理器
);
proxy.delete(); // 觸發代理流程

執行流程深度追蹤 (堆棧視角)

  1. proxy.delete() 調用
  2. JVM 分發到代理類 $Proxy0 的 delete() 方法
  3. $Proxy0.delete() 內部構造 Method 對象,調用 handler.invoke(proxy, method, args)
  4. LogInterceptor.invoke() 執行:
    • 前置邏輯:beforeMethod(method) → 打印 "delete start"
    • 核心反射調用:method.invoke(target, args)
    • 後置邏輯:打印 "delete end"
  5. method.invoke() 觸發反射機制,最終調用 UserDAOImpl.delete()
  6. 目標方法返回,反射返回,invoke() 返回,代理方法返回,調用者收到結果

$Proxy0 類結構揭秘

  • 類名格式:$Proxy + 數字,運行時由 ProxyGenerator 動態生成字節碼。
  • 繼承 java.lang.reflect.Proxy,實現目標所有介面 (UserDAO)。
  • 每個介面方法生成一個對應方法,方法體固定模板:
    
    public final void delete() throws {
        try {
            handler.invoke(this, m1, null); // m1 是 Method 對象,靜態緩存
        } catch (Error|RuntimeException e) { throw e; }
        catch (Throwable e) { throw new UndeclaredThrowableException(e); }
    }
    
  • 關鍵點:代理類不包含業務邏輯,純粹負責分發給 InvocationHandler。

動態生成過程簡析


// Proxy.newProxyInstance 核心邏輯
Class cl = getProxyClass0(loader, interfaces); // 1. 獲取/生成代理類
Constructor cons = cl.getConstructor(InvocationHandler.class); // 2. 獲取構造器
return cons.newInstance(h); // 3. 實例化,注入 handler
  • getProxyClass0 使用弱引用緩存,同介面組合只生成一次。
  • 生成字節碼使用 ProxyGenerator.generateProxyClass,輸出 .class 二進制數組,再 defineClass 載入 JVM。

動態代理 vs CGLIB 對比

Spring AOP 中的應用

Spring 默認:有介面用 JDK,無介面用 CGLIB。可通過 @EnableAspectJAutoProxy(proxyTargetClass=true) 強制 CGLIB。理解 JDK 動態代理原理,是掌握 Spring AOP 切面執行順序、代理對象注入、自調用失效 (@Transactional 失效) 等問題的基石。

調試技巧:保存生成的代理類字節碼


// JVM 參數
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
// 當前目錄下會生成 $Proxy0.class,可用 jad/fernflower 反編譯查看
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
維度JDK 動態代理CGLIB
前提條件目標必須實現一個或多個介面無需介面,繼承目標類
原理生成實現介面的類生成目標類的子類,重寫方法
final 方法介面方法不可 final無法代理 final/private 方法
性能 (歷史)早期較慢 (反射調用)早期較快 (FastClass 索引)
現狀 (JDK 8+)invokedynamic 優化,性能接近仍廣泛用於 Spring AOP (無介面場景)