1. 程式人生 > >.NET 下基於動態代理的 AOP 框架實現揭祕

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

.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

  • https://github.com/dotnetcore/AspectCore-Framework
  • https://github.com/dotnetcore/AspectCore-Framework/blob/master/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs
  • https://github.com/castleproject/Core
  • https://github.com/castleproject/Core/blob/master/src/Castle.Core/DynamicProxy/ModuleScope.cs
  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs
  • https://github.com/WeihanLi/SamplesInPractice/blob/master/AopSample/Program.cs