• C#实现AOP切面


    对于大型系统,总有一些切面型的功能要求。比如记录方法参数和返回值日志。验证用户提交,统一捕获异常,统一性能监测。很多业务都需要这样的功能。拿写日志来说,如果每个方法入口和返回处都写日志,代码就很多,不利于统一维护。

    所以需要借助设计模式的代理模式来实现切面功能。代理和我们生活中中介一个意思。对程序实现来说就是实现一个类和要被代理的类实现一样接口。代理类不做业务处理,内部创建一个被代理的类对象。每个接口实现调用内部被代理对象的。在调用内部被代理对象前后就能加上代理的杂事了。比如捕获异常、记录参数日志、记录返回值日志、计算执行时间。对于我们业务来说这些事是杂事,不应该由每个具体业务关心。

    按上面原理手工创建代理类相信大家可以实现代理模式了。但是实际中要人为给每个类创建代理类和加代理逻辑那就毫无意义了。这样比在原方法写日志还麻烦。如果一门编程语言让你能动态创建类是不是这种设计就很美好了。

    正好Java和C#都是编译的中间语言,还提供了生成中间代码的操作类。这样我们就可以借助这个功能实现AOP框架了。

    首先实现AOP先抽象拦截器接口。代理工厂把要代理的类和拦截器类按配置借助中间语言动态组装代理类返回给获取方。由于代理类和被代理类实现了相同接口。因此对获取方如果用接口定义对象,那么这个代理操作对他实际是不可见的,他以为获取的还是原生对象。

    拦截器接口

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MyAop
    {
        /// 
        /// 拦截器的接口
        /// 
        public interface IInterceptor
        {
            /// 
            /// 方法调用前
            /// 
            /// 方法名
            /// 参数
            /// 状态对象,用于调用后传入
            object BeforeCall(string operationName, object[] inputs);
    
            /// 
            /// 方法调用后
            /// 
            /// 方法名
            /// 结果
            /// 状态对象
            void AfterCall(string operationName, object returnValue, object correlationState);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    代理类构建器,生成IL中间语言

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Reflection.Emit;
    
    namespace MyAop
    {
        /// 
        /// 默认代理创建器
        /// 
        public static class DefaultProxyBuilder
        {
            /// 
            /// void类型
            /// 
            private static readonly Type VoidType = Type.GetType("System.Void");
    
            /// 
            /// 创建代理对象
            /// 
            /// 要创建代理的类型
            /// 代理类型
            public static T CreateProxy<T>()
            {
                //获得T的类型
                Type classType = typeof(T);
    
                //代理类型的命名空间
                string name = classType.Namespace + ".Aop";
                //代理类型程序集的名字
                string fileName = name + ".dll";
    
                //定义程序集的唯一标识
                var assemblyName = new AssemblyName(name);
                //定义动态程序集
                var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,AssemblyBuilderAccess.RunAndSave);
                //定义动态模块                                                                  
                var moduleBuilder = assemblyBuilder.DefineDynamicModule(name, fileName);
                //根据所给的类型和模块构建新类型(该类型的代理)
                var aopType = BulidType(classType, moduleBuilder);
                //保存程序集
                assemblyBuilder.Save(fileName);
                //返回构建的代理类型
                return (T)Activator.CreateInstance(aopType);
            }
    
            /// 
            /// 根据所给的类型和模块在该模块下构建该类型的代理类型
            /// 
            /// 所要构建代理的类型
            /// 所在模块
            /// 构建的代理类型
            private static Type BulidType(Type classType, ModuleBuilder moduleBuilder)
            {
                //代理类型的名字
                string className = classType.Name + "Proxy";
    
                //根据所给的类型定义类型
                var typeBuilder = moduleBuilder.DefineType(className,
                                                           TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class,
                                                           classType);
                //定义字段(拦截器) inspector
                var inspectorFieldBuilder = typeBuilder.DefineField("inspector", typeof(IInterceptor),
                                                                    FieldAttributes.Private | FieldAttributes.InitOnly);
                //构造代理的构造函数
                BuildCtor(classType, inspectorFieldBuilder, typeBuilder);
    
                //构造代理的方法
                BuildMethod(classType, inspectorFieldBuilder, typeBuilder);
    
                //得到构造之后的代理类型
                Type aopType = typeBuilder.CreateType();
                //返回生成的代理类型
                return aopType;
            }
    
            /// 
            /// 构造代理的方法
            /// 
            /// 原类型
            /// 拦截器
            /// 代理类型
            private static void BuildMethod(Type classType, FieldBuilder inspectorFieldBuilder, TypeBuilder typeBuilder)
            {
                //获取原类型的所有方法信息
                var methodInfos = classType.GetMethods();
                //遍历所有的方法信息
                foreach (var methodInfo in methodInfos)
                {
                    //如果不是虚方法和抽象放法,进入下一个
                    if (!methodInfo.IsVirtual && !methodInfo.IsAbstract) continue;
                    //如果是Object的ToString方法,进入下一个
                    if (methodInfo.Name == "ToString") continue;
                    //如果是Object的GetHashCode方法,进入下一个
                    if (methodInfo.Name == "GetHashCode") continue;
                    //如果是Object的Equals方法,进入下一个
                    if (methodInfo.Name == "Equals") continue;
    
                    //获取该方法的所有参数信息
                    var parameterInfos = methodInfo.GetParameters();
                    //用Lamada表达式获取每个参数的类型
                    var parameterTypes = parameterInfos.Select(p => p.ParameterType).ToArray();
                    //获取参数的个数
                    var parameterLength = parameterTypes.Length;
                    //获取方法是否有返回值
                    var hasResult = methodInfo.ReturnType != VoidType;
    
                    //创建和该方法一样的方法
                    var methodBuilder = typeBuilder.DefineMethod(methodInfo.Name,
                                                                 MethodAttributes.Public | MethodAttributes.Final |
                                                                 MethodAttributes.Virtual
                                                                 , methodInfo.ReturnType
                                                                 , parameterTypes);
                    //得到IL加载器
                    var il = methodBuilder.GetILGenerator();
    
                    //局部变量
                    il.DeclareLocal(typeof(object)); //correlationState
                    il.DeclareLocal(typeof(object)); //结果
                    il.DeclareLocal(typeof(object[])); //参数
    
                    //BeforeCall(string operationName, object[] inputs);前置通知
                    il.Emit(OpCodes.Ldarg_0);
    
                    il.Emit(OpCodes.Ldfld, inspectorFieldBuilder);//获取字段_inspector
                    il.Emit(OpCodes.Ldstr, methodInfo.Name);//参数operationName
    
                    if (parameterLength == 0)//判断方法参数长度
                    {
                        il.Emit(OpCodes.Ldnull);//null -> 参数 inputs
                    }
                    else
                    {
                        //创建new object[parameterLength];
                        il.Emit(OpCodes.Ldc_I4, parameterLength);
                        il.Emit(OpCodes.Newarr, typeof(Object));
                        il.Emit(OpCodes.Stloc_2);//压入局部变量2 parameters
    
                        for (int i = 0, j = 1; i < parameterLength; i++, j++)
                        {
                            //object[i] = arg[j]
                            il.Emit(OpCodes.Ldloc_2);
                            il.Emit(OpCodes.Ldc_I4, 0);
                            il.Emit(OpCodes.Ldarg, j);
                            if (parameterTypes[i].IsValueType) il.Emit(OpCodes.Box, parameterTypes[i]);//对值类型装箱
                            il.Emit(OpCodes.Stelem_Ref);
                        }
                        il.Emit(OpCodes.Ldloc_2);//取出局部变量2 parameters-> 参数 inputs
                    }
    
                    il.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("BeforeCall"));//调用BeforeCall
                    il.Emit(OpCodes.Stloc_0);//建返回压入局部变量0 correlationState
    
                    //Call methodInfo
                    il.Emit(OpCodes.Ldarg_0);
                    //获取参数表
                    for (int i = 1, length = parameterLength + 1; i < length; i++)
                    {
                        il.Emit(OpCodes.Ldarg_S, i);
                    }
                    il.Emit(OpCodes.Call, methodInfo);
                    //将返回值压入 局部变量1result void就压入null
                    if (!hasResult)
                    {
                        il.Emit(OpCodes.Ldnull);
                    }
                    else if (methodInfo.ReturnType.IsValueType)
                    {
                        il.Emit(OpCodes.Box, methodInfo.ReturnType);//对值类型装箱
                    }
                    il.Emit(OpCodes.Stloc_1);
    
                    //AfterCall(string operationName, object returnValue, object correlationState);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldfld, inspectorFieldBuilder);//获取字段_inspector
                    il.Emit(OpCodes.Ldstr, methodInfo.Name);//参数 operationName
                    il.Emit(OpCodes.Ldloc_1);//局部变量1 result
                    il.Emit(OpCodes.Ldloc_0);// 局部变量0 correlationState
                    il.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("AfterCall"));
    
                    //result
                    if (!hasResult)
                    {
                        il.Emit(OpCodes.Ret);
                        return;
                    }
                    il.Emit(OpCodes.Ldloc_1);//非void取出局部变量1 result
                    if (methodInfo.ReturnType.IsValueType)
                    {
                        il.Emit(OpCodes.Unbox_Any, methodInfo.ReturnType);//对值类型拆箱
                    }
                    il.Emit(OpCodes.Ret);
                }
            }
    
            /// 
            /// 构造代理的构造函数
            /// 
            /// 原类型
            /// 拦截器
            /// 代理类型
            private static void BuildCtor(Type classType, FieldBuilder inspectorFieldBuilder, TypeBuilder typeBuilder)
            {
                {
                    //定义代理的构造函数
                    var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis,
                                                                    Type.EmptyTypes);
                    //获得IL指令器
                    var il = ctorBuilder.GetILGenerator();
    
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, classType.GetConstructor(Type.EmptyTypes));//调用base的默认ctor
                    il.Emit(OpCodes.Ldarg_0);
                    //将typeof(classType)压入计算堆
                    il.Emit(OpCodes.Ldtoken, classType);
                    il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }));
    
                    #region 创建并实例化拦截器
                    //调用DefaultInterceptorFactory.Create(type)
                    il.Emit(OpCodes.Call, typeof(DefaultInterceptorFactory).GetMethod("Create", new[] { typeof(Type) }));
                    //将结果保存到字段_inspector
                    il.Emit(OpCodes.Stfld, inspectorFieldBuilder);
                    #endregion
    
                    il.Emit(OpCodes.Ret);
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232

    实现一个简单的拦截器对象

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MyAop
    {
        public class StandardInterceptor : IInterceptor
        {
    
    
            public object BeforeCall(string operationName, object[] inputs)
            {
                Console.WriteLine("Before call :{0}", operationName);
                return null;
            }
    
            public void AfterCall(string operationName, object returnValue, object correlationState)
            {
                Console.WriteLine("After call :{0} resule: {1}  State:{2}", operationName, returnValue ?? "Null", correlationState);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    实现一个简单的测试对象

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MyAop
    {
        public class Animal
        {
            public virtual string Speak(string msg)
            {
                Console.WriteLine("Animal.Speak:{0}", msg);
                return msg;
            }
    
            public virtual void Speak()
            {
                Console.WriteLine("Animal.Speak");
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试看有没有执行拦截器逻辑

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MyAop
    {
        class Program
        {
            static void Main(string[] args)
            {
                Animal animal = DefaultProxyBuilder.CreateProxy<Animal>();
                animal.Speak();
                Console.WriteLine();
                animal.Speak("Hello");
                Console.ReadLine();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    AOP

    这就是企业级运用框架spring及C#版本的spring.net的基本实现原理。IOC和AOP两大基石,DotNetCore现在已经自带实现了容器,所以不需要用Spring.Net了。

    更复杂的实现看下面的代码,已经有框架雏形了,没实际用,只是学习测试用过。
    Green

  • 相关阅读:
    PHP 8.1性能基准测试结果出炉,比7.0版本提升44%
    [MyBatis] SQL动态标签,SelecKey标签
    2020年大厂Java面试前复习的正确姿势(800+面试题答案解析)
    G1D8-GNN@lab0&APT论文&美亚2021个人赛
    web蓝桥杯真题:绝美宋词
    Stable Diffusion基础:ControlNet之重新上色(黑白照片换新颜)
    ServletContext对象
    【趣味随笔】移动机器人基础(导航方式、自主导航、硬件系统结构分布)
    无序数组计算排序后最大相邻差
    ffmpeg使用命令实现音视频分离
  • 原文地址:https://blog.csdn.net/zhanglianzhu_91/article/details/125940521