C# 使用Emit实现动态AOP框架 进阶篇之优化
目 录
在前几篇文章中,有几个遗留问题还没有处理:
1、切面特性对象的InterceptType属性没有处理,分别取值OnEntry(只触发入口)、OnExit(只触发出口)、All(都触发);
2、代理类中,各代理方法中的相关特性调用代码冗余,随着特性的增多,这些代码也成倍增加;
1 LogAttribute logAttribute = new LogAttribute(); 2 logAttribute.OnEntry(aspectContext); 3 base.Age = value; 4 logAttribute.OnExit(aspectContext);
3、AspectExceptionAttribute我们只是修改了 ILGenerateProxyMethod,实现了异常处理,但是造成的结果时所有方法和属性(Get、Set方法)都进行了异常处理;
4、一般属性切入时,我们只切入Set方法,而现在Get、Set都进行了切入,灵活性不够;
下面我们针对这四个问题进行优化:
-
增加公共的切面特性调用方法( _OnEntryExit)
方法如下:
1 protected override void _OnEntryExit(string text, AspectContext context, AspectAttribute[] array) 2 { 3 for (int i = 0; i < array.Length; i++) 4 { 5 InterceptType interceptType = array[i].InterceptType; 6 if (text.Equals("OnEntry") && interceptType.HasFlag(InterceptType.OnEntry)) 7 { 8 array[i].OnEntry(context); 9 } 10 if (text.Equals("OnExit") && interceptType.HasFlag(InterceptType.OnExit)) 11 { 12 array[i].OnExit(context); 13 } 14 } 15 }
方法调用代码如下:
public override int NYearLater(int num) { AspectContext aspectContext = new AspectContext(this, "NYearLater", new object[] { num }); AspectAttribute[] array = new AspectAttribute[] { (AspectAttribute)new LogAttribute(InterceptType.All), (AspectAttribute)new PerformanceAttribute(InterceptType.All) }; int num2; try { this._OnEntryExit("OnEntry", aspectContext, array); num2 = base.NYearLater(num); aspectContext.Result = num2; this._OnEntryExit("OnExit", aspectContext, array); } catch (Exception ex) { aspectContext.Result = ex; this._OnEntryExit("OnEntry", aspectContext, new AspectAttribute[] { (AspectAttribute)new AspectExceptionAttribute(InterceptType.All) }); } finally { this._OnEntryExit("OnExit", aspectContext, new AspectAttribute[] { (AspectAttribute)new AspectExceptionAttribute(InterceptType.All) }); } return num2; }
上边标红的代码中看以看到,除去异常处理外的所有特性先存储到一个基于其父类的AspectAttribute[]数组中,调用通用方法_OnEntryExit管理特性切入的触发,并在_OnEntryExit中处理拦截类型,有效的解决了问题1、2,下边我们看一下生成AspectAttribute[]数组和_OnEntryExit方法的Emit代码:
生成AspectAttribute[]数组:
1 private static LocalBuilder CreateAspectAttributeArray(ILGenerator il_ProxyMethod, object[] aspectAttributes) 2 { 3 int aspectCount = aspectAttributes.Length; 4 5 LocalBuilder array = il_ProxyMethod.DeclareLocal(typeof(AspectAttribute[])); 6 //数组长度入栈 7 il_ProxyMethod.Emit(OpCodes.Ldc_I4, aspectCount); 8 //生成新数组 9 il_ProxyMethod.Emit(OpCodes.Newarr, typeof(AspectAttribute)); 10 //赋值给局部数组变量 11 il_ProxyMethod.Emit(OpCodes.Stloc, array); 12 13 //遍历参数,并存入数组 14 for (int i = 0; i < aspectCount; i++) 15 { 16 il_ProxyMethod.Emit(OpCodes.Ldloc, array);//数组入栈 17 il_ProxyMethod.Emit(OpCodes.Ldc_I4, i);//数组下标入栈 18 19 var aspectType = aspectAttributes[i].GetType(); 20 21 ConstructorInfo constructor = aspectType.GetConstructor(new Type[] { typeof(InterceptType) }); 22 23 //获取当前特性拦截类型 24 il_ProxyMethod.Emit(OpCodes.Ldc_I4, (int)(aspectAttributes[i] as AspectAttribute).InterceptType); 25 26 //生成新的特性对象 27 il_ProxyMethod.Emit(OpCodes.Newobj, constructor); 28 il_ProxyMethod.Emit(OpCodes.Castclass, typeof(AspectAttribute)); 29 //存入数组 30 il_ProxyMethod.Emit(OpCodes.Stelem_Ref); 31 } 32 return array; 33 }
_OnEntryExit方法:
1 private static MethodBuilder CreateOnEntryExit(TypeBuilder typeBuilder) 2 { 3 var result = typeBuilder.DefineMethod("_OnEntryExit", MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual, 4 typeof(void), new Type[] { typeof(string), typeof(AspectContext), typeof(AspectAttribute[]) }); 5 6 ILGenerator gen = result.GetILGenerator(); 7 //临时变量i 8 LocalBuilder locI = gen.DeclareLocal(typeof(int)); 9 Label lbCondition = gen.DefineLabel(); 10 Label lbTrue = gen.DefineLabel(); 11 12 //if 标签 13 Label lbIfEntryRet = gen.DefineLabel(); 14 15 Label lbIfExitRet = gen.DefineLabel(); 16 17 //i=0 18 gen.Emit(OpCodes.Ldc_I4_0); 19 gen.Emit(OpCodes.Stloc, locI); 20 21 //跳至判断 22 gen.Emit(OpCodes.Br, lbCondition); 23 24 //标记True代码 25 gen.MarkLabel(lbTrue); 26 27 //声明拦截类型变量,存储当前切面特性的拦截类型 InterceptType interceptType = array[i].InterceptType; 28 LocalBuilder interceptType = gen.DeclareLocal(typeof(InterceptType)); 29 30 gen.Emit(OpCodes.Ldarg_3); 31 gen.Emit(OpCodes.Ldloc, locI); 32 gen.Emit(OpCodes.Ldelem_Ref); 33 gen.Emit(OpCodes.Callvirt, typeof(AspectAttribute).GetMethod("get_InterceptType")); 34 gen.Emit(OpCodes.Box, typeof(InterceptType)); 35 gen.Emit(OpCodes.Stloc, interceptType); 36 37 //if 条件 if (text.Equals("OnEntry") 38 gen.Emit(OpCodes.Ldarg_1); 39 gen.Emit(OpCodes.Ldstr, "OnEntry"); 40 gen.Emit(OpCodes.Call, typeof(String).GetMethod("Equals", new Type[] { typeof(string) })); 41 gen.Emit(OpCodes.Brfalse, lbIfEntryRet); 42 43 44 //if 条件 && interceptType.HasFlag(InterceptType.OnEntry)) 45 gen.Emit(OpCodes.Ldloc, interceptType); 46 gen.Emit(OpCodes.Ldc_I4, 1); 47 gen.Emit(OpCodes.Box, typeof(InterceptType)); 48 gen.Emit(OpCodes.Call, typeof(Enum).GetMethod("HasFlag")); 49 gen.Emit(OpCodes.Brfalse, lbIfEntryRet); 50 51 //True 52 gen.Emit(OpCodes.Ldarg_3); 53 gen.Emit(OpCodes.Ldloc, locI); 54 gen.Emit(OpCodes.Ldelem_Ref); 55 gen.Emit(OpCodes.Ldarg_2); 56 gen.Emit(OpCodes.Callvirt, typeof(AspectAttribute).GetMethod("OnEntry")); 57 58 gen.MarkLabel(lbIfEntryRet); 59 60 //if 条件 if (text.Equals("OnExit")) 61 gen.Emit(OpCodes.Ldarg_1); 62 gen.Emit(OpCodes.Ldstr, "OnExit"); 63 gen.Emit(OpCodes.Call, typeof(String).GetMethod("Equals", new Type[] { typeof(string) })); 64 gen.Emit(OpCodes.Brfalse, lbIfExitRet); 65 66 //if 条件 (&& interceptType.HasFlag(InterceptType.OnExit)) 67 gen.Emit(OpCodes.Ldloc, interceptType); 68 gen.Emit(OpCodes.Ldc_I4, 2); 69 gen.Emit(OpCodes.Box, typeof(InterceptType)); 70 gen.Emit(OpCodes.Call, typeof(Enum).GetMethod("HasFlag")); 71 gen.Emit(OpCodes.Brfalse, lbIfExitRet); 72 73 //True 74 gen.Emit(OpCodes.Ldarg_3); 75 gen.Emit(OpCodes.Ldloc, locI); 76 gen.Emit(OpCodes.Ldelem_Ref); 77 gen.Emit(OpCodes.Ldarg_2); 78 gen.Emit(OpCodes.Callvirt, typeof(AspectAttribute).GetMethod("OnExit")); 79 80 gen.MarkLabel(lbIfExitRet); 81 82 //追加代码 //i++ 83 gen.Emit(OpCodes.Ldloc, locI); 84 gen.Emit(OpCodes.Ldc_I4_1); 85 gen.Emit(OpCodes.Add); 86 gen.Emit(OpCodes.Stloc, locI); 87 88 //判断代码 89 gen.MarkLabel(lbCondition); 90 gen.Emit(OpCodes.Ldloc, locI); 91 gen.Emit(OpCodes.Ldarg_3); 92 gen.Emit(OpCodes.Ldlen); 93 gen.Emit(OpCodes.Conv_I4); 94 95 gen.Emit(OpCodes.Clt); 96 //如果True,跳至true代码 97 gen.Emit(OpCodes.Brtrue, lbTrue); 98 99 //******** 100 gen.Emit(OpCodes.Nop); 101 102 gen.Emit(OpCodes.Ret); 103 104 return result; 105 } 106 #endregion
这个方法稍微复杂了一些,有一个循环和两个判断,比直接写C#要难于理解。
-
增加两个特性,并修改ILGenerateProxyMethod方法解决问题3、4
作用于类的切面范围特性,如下:
/// <summary> /// 切面范围特性 /// </summary> [AttributeUsage(AttributeTargets.Class)] public class AspectRangeAttribute : Attribute { #region 构造函数 /// <summary> /// 构造函数 /// </summary> public AspectRangeAttribute() { Type = XAOP.AspectRangeType.Method | XAOP.AspectRangeType.Setter; } /// <summary> /// 构造函数 /// </summary> /// <param name="aspectType">切面类型</param> public AspectRangeAttribute(AspectRangeType aspectType) { Type = aspectType; } #endregion public AspectRangeType Type { get; set; } } /// <summary> /// 切面类型 /// </summary> public enum AspectRangeType { #region 枚举值 /// <summary> /// 构造函数 /// </summary> Constructor = 1, /// <summary> /// 方法 /// </summary> Method = 2, /// <summary> /// 属性的Set方法 /// </summary> Setter = 4, /// <summary> /// 属性的Get方法 /// </summary> Getter = 8, /// <summary> /// 所有 /// </summary> All = 15 #endregion }
作用于方法和属性的不切入特性:
/// <summary> /// 不切入特性 /// </summary> [AttributeUsage(AttributeTargets.Method| AttributeTargets.Property)] public class NoAspectAttribute : Attribute { }
如果切面范围特性的取值是AspectRangeType.Method | AspectRangeType.Setter;那么我们看一个属性对应的代码
public override string Name { get { return base.Name; } set { AspectContext aspectContext = new AspectContext(this, "set_Name", new object[] { value }); AspectAttribute[] array = new AspectAttribute[] { (AspectAttribute)new LogAttribute(InterceptType.OnExit) }; this._OnEntryExit("OnEntry", aspectContext, array); base.Name = value; this._OnEntryExit("OnExit", aspectContext, array); } }
我们可以看到Get方法只是重写了一下,而Set方法进行了切入,这样我们就需要一个重写方法和一个切入后的代理方法,而ILGenerateProxyMethod只实现切入方法,下边我们将去除ILGenerateProxyMethod方法,将其拆分为两个类,一个类(ILOverrideMethod)实现重写方法,一个类(ILProxyMethod)实现切入后的代理方法(它继承ILOverrideMethod),而一个方法调用大概是三个阶段,执行方法体、获取返回值、返回;下边我们看两个类的实现:
ILOverrideMethod:
1 /// <summary> 2 /// 重写方法 3 /// </summary> 4 public class ILOverrideMethod 5 { 6 #region 构造函数 7 /// <summary> 8 /// 构造函数 9 /// </summary> 10 /// <param name="srcMethod">源方法</param> 11 /// <param name="overrideIL">override方法IL</param> 12 public ILOverrideMethod(MethodInfo srcMethod, ILGenerator overrideIL) 13 { 14 SrcMethod = srcMethod; 15 16 OverrideMethodIL = overrideIL; 17 18 ParamTypes = XDynamic.GetMethodParameterTypes(SrcMethod); 19 } 20 #endregion 21 22 #region 属性 23 24 /// <summary> 25 /// 源方法 26 /// </summary> 27 public MethodInfo SrcMethod { get; set; } 28 29 /// <summary> 30 /// 重写方法IL 31 /// </summary> 32 public ILGenerator OverrideMethodIL { get; set; } 33 34 #endregion 35 36 #region Protected 37 38 /// <summary> 39 /// 返回值 40 /// </summary> 41 protected LocalBuilder Result; 42 43 /// <summary> 44 /// 方法参数列表 45 /// </summary> 46 protected Type[] ParamTypes; 47 48 #endregion 49 50 #region 生成重写方法 Create 51 /// <summary> 52 /// 生成重写方法 53 /// </summary> 54 public virtual void Create() 55 { 56 CreateMethodBody(); 57 58 GetResult(); 59 60 Return(); 61 } 62 #endregion 63 64 #region 创建方法体 CreateMethodBody 65 /// <summary> 66 /// 创建方法体 67 /// </summary> 68 protected virtual void CreateMethodBody() 69 { 70 //类对象,参数值依次入栈并调用基类的方法 71 for (int i = 0; i <= ParamTypes.Length; i++) 72 OverrideMethodIL.Emit(OpCodes.Ldarg, i); 73 74 OverrideMethodIL.Emit(OpCodes.Call, SrcMethod); 75 } 76 #endregion 77 78 #region 获取结果 GetResult 79 /// <summary> 80 /// 获取结果 81 /// </summary> 82 protected virtual void GetResult() 83 { 84 //如果有返回值,保存返回值到局部变量 85 if (SrcMethod.ReturnType != typeof(void)) 86 { 87 Result = OverrideMethodIL.DeclareLocal(SrcMethod.ReturnType); 88 OverrideMethodIL.Emit(OpCodes.Stloc, Result); 89 } 90 } 91 #endregion 92 93 #region 返回 Return 94 /// <summary> 95 /// 返回 96 /// </summary> 97 protected virtual void Return() 98 { 99 //如果有返回值,则把返回值压栈 100 if (Result != null) 101 OverrideMethodIL.Emit(OpCodes.Ldloc, Result); 102 103 OverrideMethodIL.Emit(OpCodes.Ret);//返回 104 } 105 #endregion 106 }
ILProxyMethod:
1 /// <summary> 2 /// 代理方法 3 /// </summary> 4 public class ILProxyMethod : ILOverrideMethod 5 { 6 #region 构造函数 7 /// <summary> 8 /// 构造函数 9 /// </summary> 10 /// <param name="srcMethod">源方法</param> 11 /// <param name="il"></param> 12 /// <param name="aspectAttributes">切面特性集合</param> 13 /// <param name="onEntryExit">切面入口出口统一调用方法</param> 14 public ILProxyMethod(MethodInfo srcMethod, ILGenerator il, object[] aspectAttributes, MethodInfo onEntryExit) 15 : base(srcMethod, il) 16 { 17 OnEntryExit = onEntryExit; 18 19 AspectExceptionAttributes = aspectAttributes.Where(p => p.GetType() == typeof(AspectExceptionAttribute)).ToArray(); 20 21 HandleException = AspectExceptionAttributes.Length > 0; 22 23 //生成切面上下文 24 AspectContext = CreateAspectContext(OverrideMethodIL, SrcMethod.Name, ParamTypes); 25 //生成除去异常特性外的特性数组 26 AspectAttributes = CreateAspectAttributeArray(OverrideMethodIL, 27 aspectAttributes.Where(p => p.GetType() != typeof(AspectExceptionAttribute)).ToArray()); 28 } 29 #endregion 30 31 #region Protected 32 33 /// <summary> 34 /// 切面入口出口统一调用方法 35 /// </summary> 36 protected MethodInfo OnEntryExit { get; set; } 37 38 /// <summary> 39 /// 切面上下文 40 /// </summary> 41 protected LocalBuilder AspectContext; 42 43 /// <summary> 44 /// 除去异常外的切面特性数组 45 /// </summary> 46 protected LocalBuilder AspectAttributes { get; set; } 47 48 /// <summary> 49 /// 异常特性 50 /// </summary> 51 protected object[] AspectExceptionAttributes; 52 53 /// <summary> 54 /// 是否处理异常 55 /// </summary> 56 protected bool HandleException = false; 57 58 #endregion 59 60 #region 创建方法体 CreateMethodBody 61 /// <summary> 62 /// 创建方法体 63 /// </summary> 64 protected override void CreateMethodBody() 65 { 66 if (HandleException) 67 OverrideMethodIL.BeginExceptionBlock(); 68 69 //调用横切对象的OnEntryt方法 70 CallOn_Entry_Exit(OverrideMethodIL, true, AspectContext, AspectAttributes, OnEntryExit); 71 72 base.CreateMethodBody(); 73 } 74 #endregion 75 76 #region 获取结果 GetResult 77 /// <summary> 78 /// 获取结果 79 /// </summary> 80 protected override void GetResult() 81 { 82 base.GetResult(); 83 84 if (SrcMethod.ReturnType != typeof(void)) 85 { 86 //给AspectContext的属性Result赋值 87 var resultSetMethod = typeof(AspectContext).GetMethod("set_Result"); 88 OverrideMethodIL.Emit(OpCodes.Ldloc, AspectContext); //加载AspectContext局部变量 89 OverrideMethodIL.Emit(OpCodes.Ldloc, Result);//加载返回值 90 OverrideMethodIL.Emit(OpCodes.Box, SrcMethod.ReturnType); 91 OverrideMethodIL.Emit(OpCodes.Call, resultSetMethod);//赋值 92 } 93 } 94 #endregion 95 96 #region 返回 97 /// <summary> 98 /// 99 /// </summary> 100 protected override void Return() 101 { 102 //调用横切对象的OnExit方法 103 CallOn_Entry_Exit(OverrideMethodIL, false, AspectContext, AspectAttributes, OnEntryExit); 104 105 if (HandleException) 106 { 107 AspectExceptionAttribute temp = AspectExceptionAttributes[0] as AspectExceptionAttribute; 108 109 OverrideMethodIL.BeginCatchBlock(typeof(Exception)); 110 111 //保存Exception到临时变量 112 LocalBuilder exception = OverrideMethodIL.DeclareLocal(typeof(Exception)); 113 114 OverrideMethodIL.Emit(OpCodes.Stloc, exception); 115 116 //复制Exception到Result 117 var resultSetMethod = typeof(AspectContext).GetMethod("set_Result"); 118 OverrideMethodIL.Emit(OpCodes.Ldloc, AspectContext); //加载AspectContext局部变量 119 OverrideMethodIL.Emit(OpCodes.Ldloc, exception);//错误信息 120 OverrideMethodIL.Emit(OpCodes.Box, typeof(Exception)); 121 OverrideMethodIL.Emit(OpCodes.Call, resultSetMethod);//赋值 122 123 //调用OnEntry 124 LocalBuilder array = CreateAspectAttributeArray(OverrideMethodIL, AspectExceptionAttributes); 125 126 CallOn_Entry_Exit(OverrideMethodIL, true, AspectContext, array, OnEntryExit); 127 128 //触发Finally 129 if (temp.InterceptType.HasFlag(InterceptType.OnExit)) 130 { 131 OverrideMethodIL.BeginFinallyBlock(); 132 133 //调用Exit 134 array = CreateAspectAttributeArray(OverrideMethodIL, AspectExceptionAttributes); 135 136 CallOn_Entry_Exit(OverrideMethodIL, false, AspectContext, array, OnEntryExit); 137 } 138 139 OverrideMethodIL.EndExceptionBlock(); 140 } 141 142 base.Return(); 143 } 144 #endregion 145 }
通过上边的代码我们可以看到,ILGenerateProxyMethod就拆分为一个类的三个方法,同时也灵活的处理了异常特性(if (HandleException),这里边新增了一个CallOn_Entry_Exit方法,其对应代码如下:
1 private static void CallOn_Entry_Exit(ILGenerator il_ProxyMethod, bool entry, LocalBuilder aspectContext, LocalBuilder array, MethodInfo onEntryExit) 2 { 3 il_ProxyMethod.Emit(OpCodes.Ldarg_0); 4 il_ProxyMethod.Emit(OpCodes.Ldstr, entry ? "OnEntry" : "OnExit"); 5 il_ProxyMethod.Emit(OpCodes.Ldloc, aspectContext); 6 il_ProxyMethod.Emit(OpCodes.Ldloc, array); 7 8 il_ProxyMethod.Emit(OpCodes.Call, onEntryExit); 9 } 10 #endregion
-
测试
测试类修改如下:
public class AopTest { public AopTest() { Name = "小明"; Age = 10; } public AopTest(string name, int age) { Name = name; Age = age; } [Log(InterceptType.OnExit)] public virtual string Name { get; set; } [Log(InterceptType.OnEntry)] public virtual int Age { get; set; } [Log] [AspectException] public virtual int NYearLater(int a) { int larter = Age + a; return larter; } }
结果:
Log OnExit: set_Name Log OnEntry:set_Age(10) Log OnEntry:NYearLater(10) Log OnExit: NYearLater Result: 20 Finally : NYearLater done...
比对结果,达到了我们的目的。