大家一起Aop
一、前言
1.在项目中无处不充斥着记录日志的代码,各种try catch,实在是有点看着不爽。这不,果断要想法子偷个懒儿。
二、摘要
鄙人不才,先总结一下个人想到的可实现AOP的几种思路:
1.通过继承特定实例,重写虚方法(C#中如virtual、override方法),动态构建一个该实例的子类,进行调用。
2.通过实现特定实例上的接口,动态构建一个该接口的实现类,切入AOP代码,内部包裹特定实例的方法。
3.最简单的一种方式,通过给特定实例继承MarshalByRefObject类,并且用继承RealProxy的代理类进行构造包裹。
代码比较少,有些Emit基础的童鞋应该很容易看懂,接下去直接上核心代码。
三、继承类模式

1 if (!method.IsPublic || !method.IsVirtual/*非虚方法无法重写*/|| method.IsFinal /*Final方法无法重写,如interface的实现方法标记为 virtual final*/ || IsObjectMethod(method)) return; 2 3 const MethodAttributes methodattributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual; 4 Type[] paramTypes = method.GetParameters().Select(ent => ent.ParameterType).ToArray(); 5 MethodBuilder mb = _typeBuilder.DefineMethod(method.Name, methodattributes, method.ReturnType, paramTypes); 6 ILGenerator il = mb.GetILGenerator(); 7 8 #region 初始化本地变量和返回值 9 //加载所有参数到本地object[],实例方法第一个参数是this,已排除 10 LoadArgsIntoLocalField(il, paramTypes); 11 12 //如果有返回值,定义返回值变量 13 bool isReturnVoid = method.ReturnType == typeof(void); 14 LocalBuilder result = null; 15 if (!isReturnVoid) 16 result = il.DeclareLocal(method.ReturnType); 17 18 //定义MethodInfo变量,下面会用到(传递到Aop的上下文中) 19 var methodInfo = il.DeclareLocal(typeof(MethodBase)); 20 il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod", Type.EmptyTypes)); 21 il.Emit(OpCodes.Stloc, methodInfo); 22 #endregion 23 24 #region 初始化AspectContext 25 Type contextType = typeof(AspectContext); 26 var context = il.DeclareLocal(contextType); 27 ConstructorInfo info = contextType.GetConstructor(Type.EmptyTypes); 28 il.Emit(OpCodes.Newobj, info); 29 il.Emit(OpCodes.Stloc, context); 30 #endregion 31 32 #region 给AspectContext的参数值属性ParameterArgs,MethodInfo赋值 33 il.Emit(OpCodes.Ldloc, context); 34 il.Emit(OpCodes.Ldloc_0); 35 il.Emit(OpCodes.Call, contextType.GetMethod("set_ParameterArgs")); 36 37 il.Emit(OpCodes.Ldloc, context); 38 il.Emit(OpCodes.Ldloc, methodInfo); 39 il.Emit(OpCodes.Call, contextType.GetMethod("set_MethodInfo")); 40 #endregion 41 42 AspectAttribute[] attrs = GetAspectAttributes(method); 43 int attrLen = attrs.Length; 44 LocalBuilder[] lbs = new LocalBuilder[attrLen]; 45 MethodInfo[] endInvokeMethods = new MethodInfo[attrLen]; 46 MethodInfo[] invokingExceptionMethods = new MethodInfo[attrLen]; 47 48 #region 初始化标记的切面对象,并调用切面对象的BeforeInvoke方法 49 for (int i = 0; i < attrLen; i++) 50 { 51 var tmpAttrType = attrs[i].GetType(); 52 var tmpAttr = il.DeclareLocal(tmpAttrType); 53 ConstructorInfo tmpAttrCtor = tmpAttrType.GetConstructor(Type.EmptyTypes); 54 55 il.Emit(OpCodes.Newobj, tmpAttrCtor); 56 il.Emit(OpCodes.Stloc, tmpAttr); 57 58 var beforeInvokeMethod = tmpAttrType.GetMethod("BeforeInvoke"); 59 endInvokeMethods[i] = tmpAttrType.GetMethod("AfterInvoke"); 60 invokingExceptionMethods[i] = tmpAttrType.GetMethod("InvokingException"); 61 62 il.Emit(OpCodes.Ldloc, tmpAttr); 63 il.Emit(OpCodes.Ldloc, context); 64 il.Emit(OpCodes.Callvirt, beforeInvokeMethod); 65 il.Emit(OpCodes.Nop); 66 67 lbs[i] = tmpAttr; 68 } 69 #endregion 70 71 //try 72 il.BeginExceptionBlock(); 73 74 #region 调用实现方法 75 if (!method.IsAbstract) 76 { 77 //类对象,参数值依次入栈 78 for (int i = 0; i <= paramTypes.Length; i++) 79 il.Emit(OpCodes.Ldarg, i); 80 81 //调用基类的方法 82 il.Emit(OpCodes.Call, method); 83 84 if (!isReturnVoid) 85 { 86 il.Emit(OpCodes.Stloc, result); 87 88 // 89 il.Emit(OpCodes.Ldloc, context); 90 il.Emit(OpCodes.Ldloc, result); 91 if (method.ReturnType.IsValueType) 92 il.Emit(OpCodes.Box, method.ReturnType); 93 il.Emit(OpCodes.Call, contextType.GetMethod("set_ReturnObj")); 94 } 95 } 96 #endregion 97 98 //catch 99 il.BeginCatchBlock(typeof(Exception)); 100 var exception = il.DeclareLocal(typeof(Exception)); 101 il.Emit(OpCodes.Stloc, exception); 102 103 #region 初始化ExceptionContext 104 var exceptionContentType = typeof(ExceptionContext); 105 var exceptionContent = il.DeclareLocal(exceptionContentType); 106 var exceptionContentCtor = exceptionContentType.GetConstructor(Type.EmptyTypes); 107 il.Emit(OpCodes.Newobj, exceptionContentCtor); 108 il.Emit(OpCodes.Stloc, exceptionContent); 109 #endregion 110 111 #region 给ExceptionContext的参数值属性ParameterArgs,MethodInfo,ExceptionInfo,赋值 112 il.Emit(OpCodes.Ldloc, exceptionContent); 113 il.Emit(OpCodes.Ldloc_0); 114 il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ParameterArgs")); 115 116 il.Emit(OpCodes.Ldloc, exceptionContent); 117 il.Emit(OpCodes.Ldloc, methodInfo); 118 il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_MethodInfo")); 119 120 il.Emit(OpCodes.Ldloc, exceptionContent); 121 il.Emit(OpCodes.Ldloc, exception); 122 il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ExceptionInfo")); 123 #endregion 124 125 #region 调用切面对象的InvokingException方法 126 for (int i = 0; i < attrLen; i++) 127 { 128 il.Emit(OpCodes.Ldloc, lbs[i]); 129 il.Emit(OpCodes.Ldloc, exceptionContent); 130 il.Emit(OpCodes.Callvirt, invokingExceptionMethods[i]); 131 il.Emit(OpCodes.Nop); 132 } 133 #endregion 134 //try end 135 il.EndExceptionBlock(); 136 137 #region 调用切面对象的AfterInvoke方法 138 for (int i = 0; i < attrLen; i++) 139 { 140 il.Emit(OpCodes.Ldloc, lbs[i]); 141 il.Emit(OpCodes.Ldloc, context); 142 il.Emit(OpCodes.Callvirt, endInvokeMethods[i]); 143 il.Emit(OpCodes.Nop); 144 } 145 #endregion 146 147 if (!isReturnVoid) 148 il.Emit(OpCodes.Ldloc, result); 149 150 //返回 151 il.Emit(OpCodes.Ret);
该种方式,建立在使用base.XXXMethod(arg1,arg2,...)模式来调用被Aop的对象的业务方法,关键点是使用BeginExceptionBlock(),BeginCatchBlock(typeof(Exception)),EndExceptionBlock();来动态创建try catch代码块,进行异常处理。
四、实现接口模式

1 if (!method.IsPublic || IsObjectMethod(method)) 2 return; 3 4 string methodName = method.Name; 5 6 const MethodAttributes methodattributes = MethodAttributes.Public | MethodAttributes.Virtual; 7 Type[] paramTypes = method.GetParameters().Select(ent => ent.ParameterType).ToArray(); 8 MethodBuilder methodBuilder = _typeBuilder.DefineMethod(methodName, methodattributes, method.ReturnType, paramTypes.ToArray()); 9 var il = methodBuilder.GetILGenerator(); 10 11 #region 初始化本地变量和返回值 12 //加载所有参数到本地object[] 13 LoadArgsIntoLocalField(il, paramTypes); 14 15 //如果有返回值,定义返回值变量 16 bool isReturnVoid = method.ReturnType == typeof(void); 17 LocalBuilder resultLocal = null; 18 if (!isReturnVoid) 19 resultLocal = il.DeclareLocal(method.ReturnType); 20 21 //定义MethodInfo变量,下面会用到(传递到Aop的上下文中) 22 var methodInfo = il.DeclareLocal(typeof(MethodBase)); 23 il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetCurrentMethod", Type.EmptyTypes)); 24 il.Emit(OpCodes.Stloc, methodInfo); 25 #endregion 26 27 #region 初始化AspectContext 28 29 Type contextType = typeof(AspectContext); 30 var context = il.DeclareLocal(contextType); 31 il.Emit(OpCodes.Newobj, contextType.GetConstructor(Type.EmptyTypes)); 32 il.Emit(OpCodes.Stloc, context); 33 34 #endregion 35 36 #region 给AspectContext的参数值属性ParameterArgs,MethodInfo赋值 37 il.Emit(OpCodes.Ldloc, context); 38 il.Emit(OpCodes.Ldloc_0); 39 il.Emit(OpCodes.Call, contextType.GetMethod("set_ParameterArgs")); 40 41 il.Emit(OpCodes.Ldloc, context); 42 il.Emit(OpCodes.Ldloc, methodInfo); 43 il.Emit(OpCodes.Call, contextType.GetMethod("set_MethodInfo")); 44 #endregion 45 46 AspectAttribute[] attrs = GetAspectAttributes(method); 47 int attrLen = attrs.Length; 48 LocalBuilder[] lbs = new LocalBuilder[attrLen]; 49 MethodInfo[] endInvokeMethods = new MethodInfo[attrLen]; 50 MethodInfo[] invokingExceptionMethods = new MethodInfo[attrLen]; 51 52 #region 初始化标记的切面对象,并调用切面对象的BeforeInvoke方法 53 for (int i = 0; i < attrLen; i++) 54 { 55 var tmpAttrType = attrs[i].GetType(); 56 var tmpAttr = il.DeclareLocal(tmpAttrType); 57 ConstructorInfo tmpAttrCtor = tmpAttrType.GetConstructor(Type.EmptyTypes); 58 59 il.Emit(OpCodes.Newobj, tmpAttrCtor); 60 il.Emit(OpCodes.Stloc, tmpAttr); 61 62 var beforeInvokeMethod = tmpAttrType.GetMethod("BeforeInvoke"); 63 endInvokeMethods[i] = tmpAttrType.GetMethod("AfterInvoke"); 64 invokingExceptionMethods[i] = tmpAttrType.GetMethod("InvokingException"); 65 66 il.Emit(OpCodes.Ldloc, tmpAttr); 67 il.Emit(OpCodes.Ldloc, context); 68 il.Emit(OpCodes.Callvirt, beforeInvokeMethod); 69 il.Emit(OpCodes.Nop); 70 71 lbs[i] = tmpAttr; 72 } 73 #endregion 74 75 il.BeginExceptionBlock(); 76 77 #region 调用实现方法 78 if (!method.IsAbstract) 79 { 80 il.Emit(OpCodes.Ldarg_0); 81 il.Emit(OpCodes.Ldfld, _realProxyField); 82 for (int i = 0; i < paramTypes.Length; i++) 83 il.Emit(OpCodes.Ldarg, i + 1); //arg_0为当前实例,故不添加到栈。 84 85 il.Emit(OpCodes.Call, method); 86 87 if (!isReturnVoid) 88 { 89 il.Emit(OpCodes.Stloc, resultLocal); 90 91 // 92 il.Emit(OpCodes.Ldloc, context); 93 il.Emit(OpCodes.Ldloc, resultLocal); 94 if (method.ReturnType.IsValueType) 95 il.Emit(OpCodes.Box, method.ReturnType); 96 il.Emit(OpCodes.Call, contextType.GetMethod("set_ReturnObj")); 97 } 98 } 99 #endregion 100 101 //catch 102 il.BeginCatchBlock(typeof(Exception)); 103 var exception = il.DeclareLocal(typeof(Exception)); 104 il.Emit(OpCodes.Stloc, exception); 105 106 #region 初始化ExceptionContext 107 var exceptionContentType = typeof(ExceptionContext); 108 var exceptionContent = il.DeclareLocal(exceptionContentType); 109 var exceptionContentCtor = exceptionContentType.GetConstructor(Type.EmptyTypes); 110 il.Emit(OpCodes.Newobj, exceptionContentCtor); 111 il.Emit(OpCodes.Stloc, exceptionContent); 112 #endregion 113 114 #region 给ExceptionContext的参数值属性ParameterArgs,MethodInfo,ExceptionInfo,赋值 115 il.Emit(OpCodes.Ldloc, exceptionContent); 116 il.Emit(OpCodes.Ldloc_0); 117 il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ParameterArgs")); 118 119 il.Emit(OpCodes.Ldloc, exceptionContent); 120 il.Emit(OpCodes.Ldloc, methodInfo); 121 il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_MethodInfo")); 122 123 il.Emit(OpCodes.Ldloc, exceptionContent); 124 il.Emit(OpCodes.Ldloc, exception); 125 il.Emit(OpCodes.Call, exceptionContentType.GetMethod("set_ExceptionInfo")); 126 #endregion 127 128 #region 调用切面对象的InvokingException方法 129 for (int i = 0; i < attrLen; i++) 130 { 131 il.Emit(OpCodes.Ldloc, lbs[i]); 132 il.Emit(OpCodes.Ldloc, exceptionContent); 133 il.Emit(OpCodes.Callvirt, invokingExceptionMethods[i]); 134 il.Emit(OpCodes.Nop); 135 } 136 #endregion 137 138 il.EndExceptionBlock(); 139 140 #region 调用切面对象的AfterInvoke方法 141 for (int i = 0; i < attrLen; i++) 142 { 143 il.Emit(OpCodes.Ldloc, lbs[i]); 144 il.Emit(OpCodes.Ldloc, context); 145 il.Emit(OpCodes.Callvirt, endInvokeMethods[i]); 146 il.Emit(OpCodes.Nop); 147 } 148 #endregion 149 150 if (!isReturnVoid) 151 { 152 il.Emit(OpCodes.Ldloc, resultLocal); 153 } 154 155 il.Emit(OpCodes.Ret);
该种方式,与继承类模式十分类似,唯一的区别是其保存了被Aop的实例到一个全局变量,通过该全局变量进行相应的业务方法调用。
五、通过MarshalByRefObject和RealProxy

1 public sealed class ProxyMarshalByRefObject<TClass> : RealProxy where TClass : class 2 { 3 private readonly MarshalByRefObject _target; 4 5 public ProxyMarshalByRefObject() 6 : base(typeof(TClass)) 7 { 8 _target = (MarshalByRefObject)Activator.CreateInstance(typeof(TClass)); 9 10 #if DEBUG 11 // Get 'ObjRef', for transmission serialization between application domains. 12 ObjRef myObjRef = RemotingServices.Marshal(_target); 13 // Get the 'URI' property of 'ObjRef' and store it. 14 Console.WriteLine("URI :{0}", myObjRef.URI); 15 #endif 16 } 17 18 public TClass CreateProxyType() 19 { 20 return (TClass)GetTransparentProxy(); 21 } 22 23 #region Invoke 24 public override IMessage Invoke(IMessage msg) 25 { 26 var call = (IMethodCallMessage)msg; 27 var attributes = GetAspectAttributes(call.MethodBase); 28 var context = new AspectContext 29 { 30 MethodInfo = call.MethodBase, 31 ParameterArgs = call.Args 32 }; 33 34 PreProcess(call, attributes, context); 35 36 var ctor = call as IConstructionCallMessage; 37 if (ctor != null) 38 { 39 var defaultProxy = RemotingServices.GetRealProxy(this._target); 40 defaultProxy.InitializeServerObject(ctor); 41 var tp = (MarshalByRefObject)this.GetTransparentProxy(); 42 return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp); 43 } 44 45 IMethodReturnMessage resultMsg = default(IMethodReturnMessage); 46 var methodInfo = (this._target as TClass).GetType().GetMethod(call.MethodName); 47 var newArray = call.Args.ToArray(); //拷贝一份参数本地副本,用于从实际方法中接收out ,ref参数的值 48 try 49 { 50 var resultValue = methodInfo.Invoke(_target, newArray); 51 context.ReturnObj = resultValue; 52 resultMsg = new ReturnMessage(context.ReturnObj, newArray, newArray.Length, call.LogicalCallContext, call); 53 } 54 catch (Exception ex) 55 { 56 var exceptionContext = new ExceptionContext 57 { 58 MethodInfo = context.MethodInfo, 59 ParameterArgs = context.ParameterArgs, 60 ReturnObj = context.ReturnObj, 61 ExceptionInfo = ex 62 }; 63 64 ProcessException(attributes, exceptionContext); 65 66 var resultValue = methodInfo.ReturnType.IsValueType ? Activator.CreateInstance(methodInfo.ReturnType) : null; 67 resultMsg = new ReturnMessage(resultValue, newArray, newArray.Length, call.LogicalCallContext, call); 68 } 69 70 PostProcess(call, resultMsg, attributes, context); 71 72 return resultMsg; 73 } 74 #endregion 75 }
该种方式,通过一个继承了RealProxy的类,来包裹一个继承了MarshalByRefObject的类,进行拦截该被Aop的实例的方法的调用。
该方式有个难点是异常的捕获,如果使用RemotingServices.ExecuteMessage(MarshalByRefObject target, IMethodCallMessage reqMsg),那么异常捕获不到,会直接抛出到外层。那么需要使用其他的方式来进行,并且要与方法调用的上下文对应。实现思路:
1.反射出指定的Method
2.进行Try catch
3.通过重新实例化ReturnMessage返回给是调用实例。
六、性能测试
CPU:i7-3770K CPU 3.50Hz
1000次调用
1万次调用
10万次调用
100万次调用
可以看到,在调用10万次之前,一直是继承模式遥遥领先,而在10万次之后,实现接口模式效率渐渐开始忧于继承模式。
七、尾声
代码虽简单,但实现的过程和思路曲折,绝对干货,帮您的节省写代码的数量和时间。节省了您的时间,那么您也花几秒钟点一下右下角的推荐吧。:)
最后附上源码,点此下载源码
作者:Zachary
出处:https://zacharyfan.com/archives/27.html
▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。
定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。
如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。
如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)