一、前言
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代码块,进行异常处理。
四、实现接口模式
新闻热点
疑难解答