C# 使用Emit实现动态AOP框架 进阶篇之优化

     目  录

C# 使用Emit实现动态AOP框架 (一)

C# 使用Emit实现动态AOP框架 (二)

C# 使用Emit实现动态AOP框架 (三)

C# 使用Emit实现动态AOP框架 进阶篇之异常处理

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...

比对结果,达到了我们的目的。

 

posted @ 2019-05-22 17:16  Asccode  阅读(1239)  评论(0编辑  收藏  举报