【Java】JDK動態代理實現原理

来源:https://www.cnblogs.com/shanml/archive/2023/10/15/17760856.html
-Advertisement-
Play Games

代理模式 代理模式一般包含三個角色: Subject:主題對象,一般是一個介面,定義一些業務相關的基本方法。 RealSubject:具體的主題對象實現類,它會實現Subject介面中的方法。 Proxy:代理對象,裡面包含一個RealSubject的引用,外部會通過這個代理對象,來實現RealSu ...


代理模式
代理模式一般包含三個角色:

  1. Subject:主題對象,一般是一個介面,定義一些業務相關的基本方法。
  2. RealSubject:具體的主題對象實現類,它會實現Subject介面中的方法。
  3. Proxy:代理對象,裡面包含一個RealSubject的引用,外部會通過這個代理對象,來實現RealSubject中方法的調用。

JAVA中提供了動態代理的實現,需要依賴InvocationHandler

舉個例子

Subject

首先創建一個主題對象,裡面定義一個execute方法:

public interface Subject {
    void execute();
}

RealSubject

接著創建具體的主題對象實現類,它會實現Subject的方法

public class RealSubject implements Subject {
    @Override
    public void execute() {
        System.out.println("realsubject方法執行");
    }
}

創建InvocationHandler

JDK動態代理需要依賴InvocationHandler,所以這裡創建一個ProxyInvocationHandler實現它的invoke方法,並提供了getProxy方法來獲取創建的代理對象,ProxyInvocationHandler類中引用了需要代理的目標對象,也就是RealSubject,在invoke方法中通過反射執行了RealSubject中的方法:

public class ProxyInvocationHandler implements InvocationHandler {

    /**
     * 代理的目標對象,也就是RealSubject
     */
    private Object target;

   /**
     * 構造函數
     */
    public ProxyInvocationHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("開始執行方法:" + method.getName());
        // 通過反射執行RealSubject中的方法
        Object result = method.invoke(target, args);
        System.out.println("結束執行方法:" + method.getName());
        return null;
    }

    public Object getProxy() {
        // 創建代理對象,傳入了類載入器、要代理對象的介面、InvocationHandler(this當前對象)
        return Proxy.newProxyInstance(Thread.currentThread()
                        .getContextClassLoader(), target.getClass().getInterfaces(), this);
    }
}

測試:

public class ProxyTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 創建實際的對象
        Subject subject = new RealSubject();
        // 創建InvocationHandler,這裡傳入的真是對象
        ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(subject);
        // 獲取代理對象
        Subject proxy = (Subject) invocationHandler.getProxy();
        // 執行方法
        proxy.execute();
    }
}

運行結果:

開始執行方法:execute
realsubject方法執行
結束執行方法:execute

根據輸出結果,可以看出JDK動態代理,主要是通過InvocationHandler生成一個代理對象,通過這個代理對象可以執行目標方法,執行之時,首先會進入到InvocationHandler中的invoke方法,在創建InvocationHandler時
傳入了實際的對象RealSubject,所以InvocationHandler中可以拿到真實對象,只需要在InvocationHandler中的invoke方法中通過反射執行RealSubject中對應的方法即可。

動態代理實現原理

在ProxyInvocationHandler中可以看到通過Proxy創建了一個代理對象,那麼接下來就進入到Proxy中,看一下是如何創建代理對象的:

        // 創建代理對象,傳入了類載入器、要代理對象的介面、InvocationHandler(this當前對象)
        return Proxy.newProxyInstance(Thread.currentThread()
                        .getContextClassLoader(), target.getClass().getInterfaces(), this);

Proxy

在Proxy中newProxyInstance方法創建代理對象的時候,傳入了類載入器、需要代理的對象以及InvocationHandler:

  1. 根據類載入器和需要代理的對象介面信息生成代理對象的class;
  2. 根據生成的代理類的class信息,獲取類的構造器;
  3. 通過構造器創建代理對象,並將InvocationHandler傳入;
public class Proxy implements java.io.Serializable {
    // 創建代理對象
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) throws IllegalArgumentException {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        // 1. 根據類載入器和需要代理的對象介面信息生成代理對象的class
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // 2. 獲取類構造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            // InvocationHandler
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 3. 通過構造器創建代理對象,並將InvocationHandler傳入
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
           //...
        }
    }
}
生成代理類的class

getProxyClass0中首先會進行邊界檢查,然後根據類載入器和需要代理的對象介面信息從緩存中獲取生成的代理類的calss,具體的實現在WeakCache的get方法中:

   /**
     * 代理類的緩存
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * 生成代理類的class
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 邊界檢查
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // 從緩存proxyClassCache中獲取class
        return proxyClassCache.get(loader, interfaces);
    }

   
WeakCache

WeakCache的get方法中如果根據緩存key獲取對象為空,會創建一個Factory對象賦值給Supplier,Factory是WeakCache的一個內部類,它實現了Supplier介面,然後調用Supplier的get方法來生成代理類的class,接下來進入到Factory的get方法中:

final class WeakCache<K, P, V> {
    // 獲取class
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
        expungeStaleEntries();
        // 獲取緩存key
        Object cacheKey = CacheKey.valueOf(key, refQueue);
        // 根據key獲取對象
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        // 如果為空
        if (valuesMap == null) {
            // 創建一個ConcurrentMap
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }
        // 創建subKey
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
        while (true) {
            if (supplier != null) {
                // 調用get方法獲取class
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // 如果為空,創建Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
            // 如果supplier為null
            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // 將factory賦值給supplier
                    supplier = factory;
                }
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
}
Factory

Factory是WeakCache的一個內部類,它實現了Supplier介面,在get方法中,又調用了valueFactory的apply方法創建class,valueFactory是WeakCache的一個成員變數,在WeakCache的構造函數中可以看到傳入了valueFactory對象進行初始化,那麼接下來就需要回到Proxy類中,看一下如何實例化WeakCache的:

final class WeakCache<K, P, V> {
    
    private final BiFunction<K, P, V> valueFactory;
    
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        // 初始化
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

    // Factory
    private final class Factory implements Supplier<V> {

        private final K key;
        private final P parameter;
        private final Object subKey;
        private final ConcurrentMap<Object, Supplier<V>> valuesMap;

        Factory(K key, P parameter, Object subKey,
                ConcurrentMap<Object, Supplier<V>> valuesMap) {
            this.key = key;
            this.parameter = parameter;
            this.subKey = subKey;
            this.valuesMap = valuesMap;
        }

        @Override
        public synchronized V get() { 
            // 
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                return null;
            }
            
            V value = null;
            try {
                // 調用valueFactory的apply方法創建class
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            
            ......
              
            return value;
        }
    }
}
ProxyClassFactory

Proxy中WeakCache初始化的時候使用的是ProxyClassFactory類型的factory:

public class Proxy implements java.io.Serializable {  
     /**
     * WeakCache初始化
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
}

所以調用valueFactory的apply方法的時候會進入到ProxyClassFactory的apply方法,在apply方法中會通過ProxyGenerator動態生成代理類並載入類,然後將實例化的代理類返回:

 private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 首碼
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            ......

            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成代理類
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                // 載入代理,並返回對象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
ProxyGenerator

ProxyGenerator是Proxy的一個內部類,用於動態生成class:

 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {  
      public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        // 生成class
        final byte[] var4 = var3.generateClassFile();
        // 是否保存到文件,如果開啟了之後,運行程式之後會在包下麵生成class文件
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if(var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);
                            Files.createDirectories(var3, new FileAttribute[0]);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class", new String[0]);
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }
        return var4;
    }
}
代理類的生成

由於設置了sun.misc.ProxyGenerator.saveGeneratedFiles為true,所以可以在包下麵看到生成的代理類$Proxy0:

  1. 它繼承了Proxy並實現了Subject,並且在構造函數中需要傳入InvocationHandler對象;
  2. 當執行$Proxy0中的execute方法時,實際上調用的是InvocationHandler的invoke方法;

package com.sun.proxy;

import com.example.demo.bean.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

// 動態生成了一個$Proxy0類,它繼承了Proxy並實現了Subject
public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    // 傳入InvocationHandler對象
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    // Subject的execute方法
    public final void execute() throws  {
        try {
            // 調用了InvocationHandler的invoke方法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.example.demo.bean.Subject").getMethod("execute", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

總結

JDK的動態代理實現原理是在運行中動態生成代理類,這個代理類實現了Subject介面,在對代理類進行實例化的時候,需要傳入InvocationHandler,當調用代理類的方法時,會執行InvocationHandler的invoke方法,在invoke方法中再執行真正的目標方法,從而完成代理功能。

參考

【拉勾教育】Dubbo源碼解讀與實戰-代理模式與常見實現


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android Studio簡單還原微信ui 目標 實現3-4個tab的切換效果 技術需求 activity, xdm, fragment, recyclerview 成果展示 其中聯繫人界面通過recyclerview實現了可以滑動列表 倉庫地址 https://github.com/SmileE ...
  • 報錯信息大概如下 Failed to compile with 15 errors 00:47:21These dependencies were not found: * codemirror/addon/dialog/dialog.css in ./node_modules/.pnpm/cach ...
  • 前言 這兩天在嘗試用語雀+ vuepress + github 搭建個人博客。 小破站地址 :王天的 web 進階之路 語雀作為編輯器,發佈文檔推送 github,再自動打包部署,大概流程如下。 問題 我使用的elog插件批量導出語雀文檔。elog採用的配置是所有文章平鋪導出,沒有按照語雀知識庫目錄 ...
  • 這是典型的程式業務處理的方式。——接收到請求入參後,先進行前置校驗,如果校驗失敗直接終止返回,否則才走後面的業務處理流程。 ...
  • 聊聊從單體到微服務架構服務演化過程 單體分層架構 在 Web 應用程式發展的早期,大部分工程是將所有的服務端功能模塊打包到單個巨石型(Monolith)應用中,譬如很多企業的 Java 應用程式打包為 war 包,最終會形成如下的架構: 巨石型應用易於搭建開發環境、易於測試、易於部署;其缺陷也非常明 ...
  • 一、問題分析 1.要用遞歸實現漢諾塔問題得先瞭解遞歸的兩個必要條件 (1)存在限制條件,當滿足這個條件的時候,遞歸將不再繼續 (2)每次調用遞歸之後會越來越接近這個限制條件 2.漢諾塔問題用遞歸解決的思路 (1)假設有n個大小不一樣的盤子且大盤子下麵不能有小盤子,三根柱子A,B,C (2)找到限制條 ...
  • 註解Controller和RestController @Controller //該註解只能標識一個Spring類是Spring MVC Controller處理器 @RestController //該註解是@Controller和@ResponseBody的結合體,兩個標註合用 Control ...
  • 註意:本文環境搭建請參考5.2節 6.1、視圖概述 視圖的作用是渲染數據,將模型Model中的數據展示給用戶; SpringMVC視圖的種類很多,預設有轉發視圖和重定向視圖; SpringMVC中的視圖是View介面; 當工程引入jstl的依賴,轉發視圖會自動轉換為JstlView; 若使用的視圖技 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...