打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
大家一起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 初始化ExceptionContext104             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             #endregion110 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             #endregion124 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             #endregion134             //try end135             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             #endregion146 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             #endregion100 101             //catch102             il.BeginCatchBlock(typeof(Exception));103             var exception = il.DeclareLocal(typeof(Exception));104             il.Emit(OpCodes.Stloc, exception);105 106             #region 初始化ExceptionContext107             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             #endregion113 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             #endregion127 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             #endregion137 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             #endregion149 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 DEBUG11             // 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 #endif16         }17 18         public TClass CreateProxyType()19         {20             return (TClass)GetTransparentProxy();21         }22 23         #region Invoke24         public override IMessage Invoke(IMessage msg)25         {26             var call = (IMethodCallMessage)msg;27             var attributes = GetAspectAttributes(call.MethodBase);28             var context = new AspectContext29             {30                 MethodInfo = call.MethodBase,31                 ParameterArgs = call.Args32             };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             try49             {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 ExceptionContext57                 {58                     MethodInfo = context.MethodInfo,59                     ParameterArgs = context.ParameterArgs,60                     ReturnObj = context.ReturnObj,61                     ExceptionInfo = ex62                 };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         #endregion75 }

该种方式,通过一个继承了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万次之后,实现接口模式效率渐渐开始忧于继承模式。

七、尾声

  代码虽简单,但实现的过程和思路曲折,绝对干货,帮您的节省写代码的数量和时间。节省了您的时间,那

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
JavaScript
前端常见20道高频面试题深入解析
初学iOS6 中的Core Image技术 | Ray Wenderlich
Unity3d Android SDK接入解析(一)Unity3d 与 Android之间的互相调用
java中面向对象思想(1)
使用Spring进行面向切面编程
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服