• .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

  • 相关阅读:
    log4j配置
    Fragment配合RadioGroup实现点击切换布局
    (转)[原] Android 自定义View 密码框 例子
    标题栏透明度变化
    Android 监听ScrollView的滑动
    Android进度条学习
    Android-正方形的容器
    Android添加图片到ListView或者 RecyclerView显示
    Android打开相机和打开相册
    2020新年快乐
  • 原文地址:https://www.cnblogs.com/weihanli/p/12713908.html
Copyright © 2020-2023  润新知