.NET 下基於動態代理的 AOP 框架實現揭秘

来源:https://www.cnblogs.com/weihanli/archive/2020/04/16/12713908.html
-Advertisement-
Play Games

.NET 下基於動態代理的 AOP 框架實現揭秘 Intro 之前基於 Roslyn 實現了一個簡單的條件解析引擎,想瞭解的可以看這篇文章 執行過程中會根據條件的不同會在運行時創建一個類,每一次創建都會生成一個新的程式集,我覺得這樣實現的話可能會導致載入的程式集越來越多,雖然目前我們的使用場景下不會 ...


.NET 下基於動態代理的 AOP 框架實現揭秘

Intro

之前基於 Roslyn 實現了一個簡單的條件解析引擎,想瞭解的可以看這篇文章 https://www.cnblogs.com/weihanli/p/roslyn-based-condition-eval-engine.html

執行過程中會根據條件的不同會在運行時創建一個類,每一次創建都會生成一個新的程式集,我覺得這樣實現的話可能會導致載入的程式集越來越多,雖然目前我們的使用場景下不會有很多,而且相同的條件只會生成一次,還是覺得這樣不是特別好,此時想起來了一些 AOP 框架,Aspect.Core/Castle/DispatchProxy ,他們這些 AOP 框架會生成一些代碼類,好像也沒有生成很多額外的程式集,於是打算看看這些 AOP 框架的實現,看看它們是如何生成動態代理類的

動態代理實現原理

看了這三個 AOP 框架的實現代碼之後,實現原理基本都是一樣的

都是通過創建一個 DynamicAssembly 之後在這個 DynamicAssemly 中創建要動態生成代理類,通過 Emit 創建要生成動態代理類的方法/屬性等

來個小示例

多說不如來點代碼示例:

internal class ProxyUtil
{
    private const string ProxyAssemblyName = "Aop.DynamicGenerated";
    private static readonly ModuleBuilder _moduleBuilder;
    private static readonly ConcurrentDictionary<string, Type> _proxyTypes = new ConcurrentDictionary<string, Type>();

    static ProxyUtil()
    {
        // 定義一個動態程式集
        var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(ProxyAssemblyName), AssemblyBuilderAccess.Run);
        // 創建一個動態模塊,後面創建動態代理類通過這個來創建
        _moduleBuilder = asmBuilder.DefineDynamicModule("Default");
    }

    public static Type CreateInterfaceProxy(Type interfaceType)
    {
        var proxyTypeName = $"{ProxyAssemblyName}.{interfaceType.FullName}";
        var type = _proxyTypes.GetOrAdd(proxyTypeName, name =>
        {
            // 定義要創建的類型,並實現指定類型介面
            var typeBuilder = _moduleBuilder.DefineType(proxyTypeName, TypeAttributes.Public, typeof(object), new[] { interfaceType });
            // 定義一個預設的構造方法
            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
            // 獲取介面中定義的方法
            var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
            foreach (var method in methods)
            {
                // 在動態類中定義方法,方法名稱,返回值和簽名與介面方法保持一致
                var methodBuilder = typeBuilder.DefineMethod(method.Name
                    , MethodAttributes.Public | MethodAttributes.Virtual,
                    method.CallingConvention,
                    method.ReturnType,
                    method.GetParameters()
                        .Select(p => p.ParameterType)
                        .ToArray()
                    );                

                // 獲取 ILGenerator,通過 Emit 實現方法體
                var ilGenerator = methodBuilder.GetILGenerator();
                ilGenerator.EmitWriteLine($"method [{method.Name}] is invoking...");
                ilGenerator.Emit(OpCodes.Ret);
                
                // 定義方法實現
                typeBuilder.DefineMethodOverride(methodBuilder, method);
            }

            return typeBuilder.CreateType();
        });
        return type;
    }
}

通過上面的定義我們可以創建一個簡單的代理類,然後定義一個 ProxyGenerator 來創建代理

public class ProxyGenerator
{
    public static readonly ProxyGenerator Instance = new ProxyGenerator();

    public object CreateInterfaceProxy(Type interfaceType)
    {
        var type = ProxyUtil.CreateInterfaceProxy(interfaceType);
        return Activator.CreateInstance(type);
    }
}
// 定義泛型擴展
public static class ProxyGeneratorExtensions
{
    public static TInterface CreateInterfaceProxy<TInterface>(this ProxyGenerator proxyGenerator) =>
        (TInterface)proxyGenerator.CreateInterfaceProxy(typeof(TInterface));
}

使用示例:

var testService = ProxyGenerator.Instance.CreateInterfaceProxy<ITestService>();
testService.Test();

可以看到這個類型就是我們動態創建的一個類型,輸出結果也是我們定義在代理類中的結果

More

.NET 中的基於動態代理的 AOP 也是這樣實現的,實現的原理大致就是這樣,這個示例比較簡單還沒有涉及 AOP ,這隻是一個簡單的動態代理示例 ,AOP 只需要在原始方法執行的邏輯上包裝一層攔截器增加對攔截器的處理和調用即可,暫時還沒實現,後面有機會再分享

Reference


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

-Advertisement-
Play Games
更多相關文章
  • 創建動態Web項目 1、創建動態Web項目: 打開Eclipse,在Package Explorer右擊,創建項目,選擇動態Web項目\( Dynamic Web Project )。 填寫項目名稱,並選擇動態Web模塊的版本( Dynamic web module version ),這裡選擇3. ...
  • ASP.NET團隊如期3.16在官方博客發佈了 Blazor WebAssembly 3.2.0 Preview 4:https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-4-release-now-availa... ...
  • 摘要 插入摘要右側圖片false摘要 插入摘要右側圖片false摘要 插入摘要右側圖片false摘要 插入摘要右側圖片false摘要 插入摘要右側圖片false摘要 插入摘要右側圖片false摘要 插入摘要右側圖片false ...
  • 0. 前言 上一篇文章介紹了字元串自身的一些方法,就是對象方法。在字元串體系中,還有一些是 類提供的靜態方法。這兩部分構成了字元串體系,當然還有一些三方庫為字元串提供了擴展方法。 這裡簡單的介紹一下 類的靜態方法。 1. 玩轉創建字元串 1.1 Create一個字元串 通過調用 方法可以生成一個字元 ...
  • 說到lock鎖,我相信在座的各位沒有不會用的,而且還知道怎麼用不會出錯,但讓他們聊一聊為什麼可以鎖住,都說人以群分,大概就有了下麵低中高水平的三類人吧。 第一類人 將lock對象定義成static,這樣就能讓多個線程看到同一個對象,以此實現線程間互斥和保證同步,如果再深問為什麼?就怕遮遮掩掩的說好像 ...
  • 分散式緩存由一個服務端實現管理和控制,有多個客戶端節點存儲數據,可以進一步提高數據的讀取速率。那麼我們要讀取某個數據的時候,應該選擇哪個節點呢?如果挨個節點找,那效率就太低了。因此需要根據一致性哈希演算法確定數據的存儲和讀取節點。以數據D,節點總個數N為基礎,通過一致性哈希演算法計算出數據D對應的哈希值 ...
  • class TestClass { public static void test(params int[] array) { string s;foreach (int i in array) { Console.WrriteLine(i); } } } public static void Ma ...
  • 一、邏輯運算符說明 邏輯運算符,顧名思義就是邏輯判斷,即結果為真或假 二、 &和&&、 |和||之間的區別 &和&&: 相同點 : 兩者都是表達當左右兩邊操作數都為真時,才為真. 不同點 : &運算符需要判斷兩邊的操作數,而&&運算符則是根據第一個操作數的真假進而判斷第二個操作數,也就是說當第一個操 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...