淺析DispatchProxy動態代理AOP

2022-06-16 12:03:54

淺析DispatchProxy動態代理AOP(程式碼原始碼)

最近學習了一段時間Java,瞭解到Java實現動態代理AOP主要分為兩種方式JDKCGLIB,我之前使用NET實現AOP切面程式設計,會用Filter攔截器Attribute特性中介軟體繼承父類別重寫父類別方法

經過查詢資料接觸到了(牛逼不分先後)

DispatchProxy類介紹

DispatchProxy原始碼,主要使用了Emit類庫直接編寫IL語言,動態生成類,和方法,然後會呼叫Invoke方法(效能很高,幾乎和我們寫的C#編譯成IL沒有區別),我們繼承了DispatchProxy抽象類,重寫Invoke方法,具體實現就可以自定義了。

DispatchProxy原始碼

DispatchProxy主要是Activator(檔案地址)以及AssemblyBuilder(檔案地址)實現,

  • Activator 類在執行時可以動態構造物件
  • AssemblyBuilder 類在執行時可以動態獲取和設定專用欄位的屬性、初始化專用欄位的建構函式,動態執行方法並返回結果。

    // 動態代理生成類
    internal static class DispatchProxyGenerator
    {
        // 動態代理容器(儲存已經生成過的代理)
        private static readonly Dictionary<Type, Dictionary<Type, Type>> s_baseTypeAndInterfaceToGeneratedProxyType = new Dictionary<Type, Dictionary<Type, Type>>();
        private static readonly ProxyAssembly s_proxyAssembly = new ProxyAssembly();
        private static readonly MethodInfo s_dispatchProxyInvokeMethod = typeof(DispatchProxy).GetTypeInfo().GetDeclaredMethod("Invoke");

        // 返回派生自'baseType'的代理的新範例,並實現'interfaceType' 
        internal static object CreateProxyInstance(Type baseType, Type interfaceType)
        {
            Debug.Assert(baseType != null);
            Debug.Assert(interfaceType != null);
            // 獲取代理型別
            Type proxiedType = GetProxyType(baseType, interfaceType);
            // 建立範例
            return Activator.CreateInstance(proxiedType, (Action<object[]>)DispatchProxyGenerator.Invoke);
        }

        // 首先從代理容器中獲取,如果沒有進行建立
        private static Type GetProxyType(Type baseType, Type interfaceType)
        {
            // 鎖住容器
            lock (s_baseTypeAndInterfaceToGeneratedProxyType)
            {
                Dictionary<Type, Type> interfaceToProxy = null;
                // 判斷baseType實現類型別容器中是否存在,不存在先初始化一個
                if (!s_baseTypeAndInterfaceToGeneratedProxyType.TryGetValue(baseType, out interfaceToProxy))
                {
                    interfaceToProxy = new Dictionary<Type, Type>();
                    s_baseTypeAndInterfaceToGeneratedProxyType[baseType] = interfaceToProxy;
                }
                Type generatedProxy = null;
                // 判斷是否存在interfaceType介面型別代理類,不存在就建立一個
                if (!interfaceToProxy.TryGetValue(interfaceType, out generatedProxy))
                {
                    generatedProxy = GenerateProxyType(baseType, interfaceType);
                    interfaceToProxy[interfaceType] = generatedProxy;
                }

                return generatedProxy;
            }
        }

        // 生成一個派生自'baseType'的新代理型別,並實現'interfaceType' 
        private static Type GenerateProxyType(Type baseType, Type interfaceType)
        {
            TypeInfo baseTypeInfo = baseType.GetTypeInfo();

            // 介面型別必須是介面,而不是類 
            if (!interfaceType.GetTypeInfo().IsInterface)
            {
                throw new ArgumentException(SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName), "T");
            }

            // 基本類型不能密封,因為代理需要將其子類化。 
            if (baseTypeInfo.IsSealed)
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Sealed, baseTypeInfo.FullName), "TProxy");
            }

            // 基本類型不能是抽象型別
            if (baseTypeInfo.IsAbstract)
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName), "TProxy");
            }

            // 基本類型必須有一個公共預設屬性(不然沒啥意義)
            if (!baseTypeInfo.DeclaredConstructors.Any(c => c.IsPublic && c.GetParameters().Length == 0))
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName), "TProxy");
            }

            // 建立baseType類 
            ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType);

            // 獲取介面中需要實現的資訊,動態新增實現
            foreach (Type t in interfaceType.GetTypeInfo().ImplementedInterfaces)
                pb.AddInterfaceImpl(t);

            // 新增實現
            pb.AddInterfaceImpl(interfaceType);

            // 實現完介面,建立該型別
            Type generatedProxyType = pb.CreateType();
            return generatedProxyType;
        }

        // 呼叫(抽象的)DispatchProxy.Invoke()方法。 
        private static void Invoke(object[] args)
        {
            PackedArgs packed = new PackedArgs(args);
            MethodBase method = s_proxyAssembly.ResolveMethodToken(packed.DeclaringType, packed.MethodToken);
            // 方法是否泛型方法定義
            if (method.IsGenericMethodDefinition)
                // 建立泛型方法定義
                method = ((MethodInfo)method).MakeGenericMethod(packed.GenericTypes);

            // 呼叫(抽象的)DispatchProxy.Invoke()方法
            try
            {
                Debug.Assert(s_dispatchProxyInvokeMethod != null);
                // 執行packed.DispatchProxy該類方法,獲取方法的返回結果
                object returnValue = s_dispatchProxyInvokeMethod.Invoke(packed.DispatchProxy,
                                                                       new object[] { method, packed.Args });
                // 執行返回結果
                packed.ReturnValue = returnValue;
            }
            catch (TargetInvocationException tie)
            {
                ExceptionDispatchInfo.Capture(tie.InnerException).Throw();
            }
        }

DispatchProxy拓展封裝

根據前面的介紹,對DispatchProxy有了一定的瞭解,我們可以進行一些封裝方便以後使用。

DynamicProxy動態代理類

我們建立DynamicProxy類,包裝方法執行之前&執行之後的處理,主體方法報錯的處理,形成一個動態代理類。

    public class DynamicProxy : DispatchProxy, IScopedDependency
    {
        private static ILogger<DynamicProxy>? _logger { get; set; }

        /// <summary>
        /// 目標類
        /// </summary>
        public object Target { get; set; }

        /// <summary>
        /// 動作之後執行
        /// </summary>
        private Action<object?[]?> _afterAction { get; set; }

        /// <summary>
        /// 動作之前執行
        /// </summary>
        private Action<object?[]?, object> _beforeAction { get; set; }

        /// <summary>
        /// 目標方法異常(預設丟擲異常資訊)
        /// </summary>
        private Action<MethodInfo?, object?[]?, Exception> _methodExceptionAction { get; set; } = (methodInfo, args, ex) => throw ex;

        /// <summary>
        /// 執行方法
        /// </summary>
        /// <param name="targetMethod">目標方法</param>
        /// <param name="args">方法引數</param>
        /// <returns></returns>
        protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
        {
            // 異常資訊
            Exception exception = null;
            // 方法執行前處理
            AfterAction(args);
            // 方法執行結果
            object resultValue = null;
            if (targetMethod != null)
            {
                try
                {
                    //呼叫實際目標物件的方法
                    resultValue = targetMethod.Invoke(Target, args);
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Invoke=>呼叫實際目標物件的方法出現錯誤:{ex.Message},{ex.StackTrace}");
                    _methodExceptionAction(targetMethod, args, ex);
                }
            }
            // 方法執行後處理
            BeforeAction(args, resultValue);

            // 判斷主體方法執行是否異常
            if (exception != null)
            {
                throw exception;
            }

            return resultValue;
        }

        /// <summary>
        /// 建立代理範例
        /// </summary>
        /// <param name="target">代理的介面型別</param>
        /// <param name="afterAction">方法執行前執行的事件</param>
        /// <param name="beforeAction">方法執行後執行的事件</param>
        /// <returns></returns>
        public T Create<T>(T target,
            Action<object?[]?> afterAction,
            Action<object?[]?, object> beforeAction,
            Action<MethodInfo?, object?[]?, Exception> targetMethodExceptionAction,
            ILogger<DynamicProxy> logger)
        {
            // DispatchProxy.Create建立T物件
            object proxy = Create<T, DynamicProxy>();
            _logger = logger;
            DynamicProxy proxyDecorator = (DynamicProxy)proxy;
            proxyDecorator.Target = target;
            proxyDecorator._afterAction = afterAction;
            proxyDecorator._beforeAction = beforeAction;
            proxyDecorator._methodExceptionAction = targetMethodExceptionAction;
            return (T)proxy;
        }

        private void AfterAction(object?[]? args)
        {
            if (_afterAction == null)
            {
                return;
            }

            try
            {
                _afterAction.Invoke(args);
            }
            catch (Exception ex)
            {
                _logger.LogError($"AfterAction=>執行之前異常:{ex.Message},{ex.StackTrace}");
            }
        }

        private void BeforeAction(object?[]? args, object? result)
        {
            if (_beforeAction == null)
            {
                return;
            }

            try
            {
                _beforeAction.Invoke(args, result);
            }
            catch (Exception ex)
            {
                _logger.LogError($"BeforeAction=>執行之後異常:{ex.Message},{ex.StackTrace}");
            }
        }
    }

建立IProxyHandle

我們會有很多處理實現類,每個實現類都需要實現AfterActionBeforeActionTargetMethodExceptionAction

   public interface IProxyHandle
    {
        /// <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 resultValue);

        /// <summary>
        /// 方法執行錯誤處理
        /// </summary>
        /// <param name="targetMethod">目標方法</param>
        /// <param name="args">目標方法引數</param>
        /// <param name="ex">目標方法執行結果</param>
        void MethodExceptionAction(MethodInfo? targetMethod, object?[]? args, Exception ex);
    }

建立ProxyHandleAttribute特性

這裡我嘗試過幾種方式,通過介面或者使用特性,最終特性比較方便團隊使用(介面也不錯)。

    /// <summary>
    /// 代理Aop特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ProxyHandleAttribute : Attribute
    {
        public Type Type { get; set; }
        public ProxyHandleAttribute(Type type)
        {
            this.Type = type;
        }
    }

ProxyFactory代理工廠

根據實現類獲取Aop切面型別,再獲取AOP工廠(IEnumerable<IProxyHandle>)中對應實現。

    /// <summary>
    /// 代理工廠
    /// </summary>
    public class ProxyFactory : IScopedDependency
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IEnumerable<IProxyHandle> _proxyHandleList;
        private readonly DynamicProxy _dynamicProxy;
        private readonly ILogger<DynamicProxy> _logger;
        public ProxyFactory(IEnumerable<IProxyHandle> proxyHandleList,
            IServiceProvider serviceProvider,
            DynamicProxy dynamicProxy,
            ILogger<DynamicProxy> logger)
        {
            this._serviceProvider = serviceProvider;
            this._proxyHandleList = proxyHandleList;
            this._dynamicProxy = dynamicProxy;
            this._logger = logger;
        }

        /// <summary>
        /// 建立代理範例
        /// </summary>
        /// <returns></returns>
        public T Create<T>() where T : class
        {

            var target = _serviceProvider.GetService<T>();
            if (target == null)
            {
                throw new BusinessException($"執行ProxyFactory=》Create方法:{typeof(T).FullName}未注入");
            }
            var type = target.GetType();
            var proxyHandleAttribute = type.GetCustomAttribute<ProxyHandleAttribute>();
            if (proxyHandleAttribute == null)
            {
                throw new BusinessException($"執行ProxyFactory=》Create方法:{type.FullName}需要新增ProxyHandleAttribute特性");
            }
            var proxyHandle = _proxyHandleList.FirstOrDefault(x => x.GetType() == proxyHandleAttribute.Type);
            if (proxyHandleAttribute == null)
            {
                throw new BusinessException($"執行ProxyFactory=》Create方法:沒有找到對應IProxyHandle介面實現");
            }
            //建立代理類
            var proxy = _dynamicProxy.Create(target,
                proxyHandle.AfterAction,
                proxyHandle.BeforeAction,
                proxyHandle.MethodExceptionAction,
                _logger);
            return proxy;
        }
    }

DynamicProxy使用範例

  • IProxyHandle實現類(ProxyHandleTest)
public class ProxyHandleTest : IProxyHandle
    {
        public void AfterAction(object[] args)
        {
            Console.WriteLine($"ProxyHandleTest=》AfterAction方法執行,args:{JsonSerializer.Serialize(args)}");
        }

        public void BeforeAction(object[] args, object resultValue)
        {
            Console.WriteLine($"ProxyHandleTest=》BeforeAction方法執行,args:{JsonSerializer.Serialize(args)},result:{resultValue}");
        }

        public void MethodExceptionAction(MethodInfo targetMethod, object[] args, Exception ex)
        {
            Console.WriteLine($"ProxyHandleTest=》MethodExceptionAction方法執行,targetMethod,:{targetMethod.Name},args:{JsonSerializer.Serialize(args)},ex:{ex.Message}");
        }
    }
  • 準備ITestServiceTestService
public interface ITestService
    {
        /// <summary>
        /// 獲取使用者Id資訊
        /// </summary>
        /// <returns></returns>
        int GetUserId();

        void SetUserId(int userId);
    }

    [ProxyHandle(typeof(ProxyHandleTest))]
    public class TestService : ITestService, IScopedDependency
    {
        public int GetUserId()
        {
            Console.WriteLine($"執行TestService=>GetUserId()");
            return 10;
        }

        public void SetUserId(int userId)
        {
            Console.WriteLine($"執行TestService=>SetUserId({userId})");
            throw new Exception("執行TestService=>SetUserId測試異常");
        }
    }
  • 最後只需要注入ProxyFactory
    public class TestController : AbpController
    {
        private readonly ProxyFactory _proxyFactory;
        public TestController(ProxyFactory proxyFactory)
        {
            _proxyFactory = proxyFactory;
        }

        // GET: api/<TestController>
        [HttpGet]
        public IEnumerable<string> Get()
        {
            // 從工廠獲取代理類
            var testService = _proxyFactory.Create<ITestService>();
            testService.GetUserId();
            return new string[] { "value1", "value2" };
        }

總結

  • DispatchProxy 實現動態代理主要是依賴Emit類庫直接編寫IL語言。
  • Activator 類在執行時動態構造物件。
  • AssemblyBuilder 類執行時動態一個獲取和設定專用欄位的屬性、初始化專用欄位的建構函式,可以動態執行方法並返回結果。

不管是Java還是Net實現AOP切面程式設計原理差不多。