.NET Core 實現動態代理做AOP(面向切面程式設計)

極客Bob發表於2021-12-27

1.介紹

1.1 動態代理作用

  用動態代理可以做AOP(面向切面程式設計),進行無入侵式實現自己的擴充套件業務,呼叫者和被呼叫者之間的解耦,提高程式碼的靈活性和可擴充套件性,比如:日誌記錄、效能統計、安全控制、事務處理、異常處理等等。本方式實現思路用的.NET Core原生的DispatchProxy類,再加《特性標記》+《Handle介面》達到無入侵式擴充套件 ,有興趣的朋友,自行改進一下,封裝成元件。

  有什麼做的不好的或者建議,希望大家及時提出,幫助改進。

  程式碼上傳在gitee:https://gitee.com/luoxiangbao/dynamic-proxy.git

1.2 原生DispatchProxy類介紹

  DispatchProxy我去看了一下原始碼,和我設想的差不多,就是Emit類庫直接編寫IL語言,動態生成類和方法,然後在方法裡呼叫Invoke方法,這個時候就我們只需要重寫Invoke方法,具體實現由我們自己管控。其效能很高,幾乎和我們寫好的C#編譯成IL沒多大區別,大家用的Autofac的AOP,我也看了一下,底層用的是Castle.Core類庫,而Castle.Core底層還是用的Emit方式實現,只是思路不同。

便於理解我給大家貼一下原始碼:

1.定義抽象DispatchProxy類的Invoke後設資料

 2.Emit類庫直接編寫IL語言,為代理類新增呼叫Invoke方法程式碼

1.3簡單介紹一下:IL程式碼

  IL是.NET框架中間語言(Intermediate Language),編譯器可以直接將源程式編譯為.exe或.dll檔案,而CLR(公共語言執行時)執行的是IL語言,不是C#高階程式語言,IL程式碼是一種近似於指令式的程式碼語言,與組合語言比較相近,給大家做個案例對比一下。

C#程式碼:

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }

IL程式碼:

IL_0000:  nop
IL_0001:  ldstr      "Hello World!"
IL_0006:  call       void [System.Console]System.Console::WriteLine(string)
IL_000b:  nop
IL_000c:  ret

  有興趣的朋友自己也可以去實現。接下來進入正題,我們怎麼利用DispatchProxy自己造輪子!!!

2.實現

 2.1 繼承DispatchProxy

   核心類就是,DispatchProxy。這是.NET core 原生的。會幫我們建立一個代理類

internal class DynamicProxy<T> : DispatchProxy
    {
        public T? decorated { get; set; }//目標類
        public Action<object?[]?>? _afterAction { get; set; }  // 動作之後執行
        public Action<object?[]?, object>? _beforeAction { get; set; } // 動作之前執行
        protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
        {
            Exception exception = null;
            AfterAction(args);
            object result = null;
            try
            {
                //呼叫實際目標物件的方法
                result = targetMethod?.Invoke(decorated, args);
            }
            catch (Exception ex)
            {
                exception = ex;
            }
            BeforeAction(args, result);
            //呼叫完執行方法後的委託,如果有異常,丟擲異常
            if (exception != null)
            {
                throw exception;
            }
            return result;
        }

        /// <summary>
        /// 建立代理例項
        /// </summary>
        /// <param name="decorated">代理的介面型別</param>
        /// <param name="afterAction">方法執行前執行的事件</param>
        /// <param name="beforeAction">方法執行後執行的事件</param>
        /// <returns></returns>
        public T Create(T decorated, Action<object?[]?> afterAction, Action<object?[]?, object> beforeAction)
        {
            object proxy = Create<T, DynamicProxy<T>>(); // 呼叫DispatchProxy 的Create  建立一個新的T
            DynamicProxy<T> proxyDecorator = (DynamicProxy<T>)proxy;
            proxyDecorator.decorated = decorated;
            //把自定義的方法委託給代理類
            proxyDecorator._afterAction = afterAction;
            proxyDecorator._beforeAction = beforeAction;
            return (T)proxy;
        }


        private void AfterAction(object?[]? args)
        {
            try
            {
                _afterAction.Invoke(args);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"執行之前異常:{ex.Message},{ex.StackTrace}");
            }
        }

        private void BeforeAction(object?[]? args, object? result)
        {
            try
            {
                _beforeAction.Invoke(args, result);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"執行之後異常:{ex.Message},{ex.StackTrace}");
            }
        }


    }

2.2 定義handle介面

  這個介面定義:執行之前、執行之後兩個方法。用來實現具體業務邏輯的處理

    internal interface IInterceptor
    {
        /// <summary>
        /// 執行之前
        /// </summary>
        /// <param name="args">引數</param>
        void AfterAction(object?[]? args);


        /// <summary>
        /// 執行之後
        /// </summary>
        /// <param name="args">引數</param>
        /// <param name="result">結果</param>
        void BeforeAction(object?[]? args, object result);
    }

2.3 定義AOP特性

  1.用來標記類具體使用哪個handle的實現來處理業務。

  2. 特性定義Type屬性決定建立代理類的時候,具體使用哪個handle實現

    [AttributeUsage(AttributeTargets.Class)]
    internal class InterceptAttribut : Attribute
    {
        public Type Type { get; set; }
        public InterceptAttribut(Type type) 
        {
            this.Type = type;
        }
    }

2.4 定義建立代理類的工廠

  這裡就是來組裝代理類與handle實現的地方。

internal class ProxyFactory
    {

        /// <summary>
        /// 建立代理例項
        /// </summary>
        /// <param name="decorated">代理的介面型別</param>
        /// <returns></returns>
        public static T Create<T>()
        {
            var decorated = ServiceHelp.GetService<T>();
            var type = decorated.GetType();
            var interceptAttribut = type.GetCustomAttribute<InterceptAttribut>();
            var interceptor = ServiceHelp.GetService<IInterceptor>(interceptAttribut.Type);
            //建立代理類
            var proxy = new DynamicProxy<T>().Create(decorated, interceptor.AfterAction, interceptor.BeforeAction);
            return proxy;
        }

    }

  1.拿到具體類,獲取Type,獲取我們上面定義的特性,通過特性的屬性,用來建立handle例項
  2.ServiceHelp是我定義的一個來獲取例項化的容器幫助類。這個用.NET CORE 原始的IOC。大家可替換成autofac
  3.建立化代理例項,把例項和handle實現的具體方法:AfterAction、BeforeAction傳入。用於代理類執行的時候,進行真正的呼叫

2.5 定義ServiceHelp

  這裡大家可自行發揮

 public static class ServiceHelp
    {

        public static IServiceProvider? serviceProvider { get; set; }

        public static void BuildServiceProvider(IServiceCollection serviceCollection)
        {
            //構建容器
            serviceProvider = serviceCollection.BuildServiceProvider();
        }

        public static T GetService<T>(Type serviceType)
        {
            return (T)serviceProvider.GetService(serviceType);
        }

        public static T GetService<T>()
        {
            return serviceProvider.GetService<T>();
        }
    }

3.測試

3.1 定義handle實現

    internal class AOPTest : IInterceptor
    {
        public void AfterAction(object?[]? args)
        {
            Console.WriteLine($"方法執行之前,args:{args}");
        }

        public void BeforeAction(object?[]? args, object result)
        {
            Console.WriteLine($"方法執行之後,args:{args},result:{result}");
            throw new NotImplementedException();
        }
    }

3.2 定義Service介面

    internal interface ITestService
    {
        public int Add(int a, int b);
    }

3.3實現Service介面

  定義實現,並且在類上加上,AOP交給哪個handle

    [InterceptAttribut(typeof(AOPTest))]
    internal class TestService : ITestService
    {
        public int Add(int a, int b)
        {
            throw new Exception("測試異常");
            return a + b;
        }
    }

3.4 大功告成

  1.建立容器,把我們自己的業務實現都註冊好

  2.通過工廠進行,動態建立代理例項

// See https://aka.ms/new-console-template for more information
using Infrastructure.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;

Console.WriteLine("Hello, World!");
//容器註冊
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddTransient(typeof(AOPTest));
serviceCollection.AddTransient<ITestService, TestService>();
//構建容器
ServiceHelp.BuildServiceProvider(serviceCollection);
//用工廠獲取代理例項
var s = ProxyFactory.Create<ITestService>();
var sum = s.Add(1, 2);
Console.WriteLine("執行完畢=====>" + sum);

4.Demo

  大家可直接訪問我的,gitee

  https://gitee.com/luoxiangbao/dynamic-proxy.git

相關文章