一、前言
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代碼塊,進行異常處理。
四、實現接口模式
新聞熱點
疑難解答