目 录
在前几篇文章中,有几个遗留问题还没有处理:
1、切面特性对象的InterceptType属性没有处理,分别取值OnEntry(只触发入口)、OnExit(只触发出口)、All(都触发);
2、代理类中,各代理方法中的相关特性调用代码冗余,随着特性的增多,这些代码也成倍增加;
LogAttribute logAttribute = new LogAttribute();
logAttribute.OnEntry(aspectContext);
base.Age = value;
logAttribute.OnExit(aspectContext);
3、AspectExceptionAttribute我们只是修改了 ILGenerateProxyMethod,实现了异常处理,但是造成的结果时所有方法和属性(Get、Set方法)都进行了异常处理;
4、一般属性切入时,我们只切入Set方法,而现在Get、Set都进行了切入,灵活性不够;
下面我们针对这四个问题进行优化:
增加公共的切面特性调用方法( _OnEntryExit)
方法如下:
protected override void _OnEntryExit(string text, AspectContext context, AspectAttribute[] array)
{
for (int i = ; i < array.Length; i++)
{
InterceptType interceptType = array[i].InterceptType;
if (text.Equals("OnEntry") && interceptType.HasFlag(InterceptType.OnEntry))
{
array[i].OnEntry(context);
}
if (text.Equals("OnExit") && interceptType.HasFlag(InterceptType.OnExit))
{
array[i].OnExit(context);
}
}
}
方法调用代码如下:
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[]数组:
private static LocalBuilder CreateAspectAttributeArray(ILGenerator il_ProxyMethod, object[] aspectAttributes)
{
int aspectCount = aspectAttributes.Length; LocalBuilder array = il_ProxyMethod.DeclareLocal(typeof(AspectAttribute[]));
//数组长度入栈
il_ProxyMethod.Emit(OpCodes.Ldc_I4, aspectCount);
//生成新数组
il_ProxyMethod.Emit(OpCodes.Newarr, typeof(AspectAttribute));
//赋值给局部数组变量
il_ProxyMethod.Emit(OpCodes.Stloc, array); //遍历参数,并存入数组
for (int i = ; i < aspectCount; i++)
{
il_ProxyMethod.Emit(OpCodes.Ldloc, array);//数组入栈
il_ProxyMethod.Emit(OpCodes.Ldc_I4, i);//数组下标入栈 var aspectType = aspectAttributes[i].GetType(); ConstructorInfo constructor = aspectType.GetConstructor(new Type[] { typeof(InterceptType) }); //获取当前特性拦截类型
il_ProxyMethod.Emit(OpCodes.Ldc_I4, (int)(aspectAttributes[i] as AspectAttribute).InterceptType); //生成新的特性对象
il_ProxyMethod.Emit(OpCodes.Newobj, constructor);
il_ProxyMethod.Emit(OpCodes.Castclass, typeof(AspectAttribute));
//存入数组
il_ProxyMethod.Emit(OpCodes.Stelem_Ref);
}
return array;
}
_OnEntryExit方法:
private static MethodBuilder CreateOnEntryExit(TypeBuilder typeBuilder)
{
var result = typeBuilder.DefineMethod("_OnEntryExit", MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual,
typeof(void), new Type[] { typeof(string), typeof(AspectContext), typeof(AspectAttribute[]) }); ILGenerator gen = result.GetILGenerator();
//临时变量i
LocalBuilder locI = gen.DeclareLocal(typeof(int));
Label lbCondition = gen.DefineLabel();
Label lbTrue = gen.DefineLabel(); //if 标签
Label lbIfEntryRet = gen.DefineLabel(); Label lbIfExitRet = gen.DefineLabel(); //i=0
gen.Emit(OpCodes.Ldc_I4_0);
gen.Emit(OpCodes.Stloc, locI); //跳至判断
gen.Emit(OpCodes.Br, lbCondition); //标记True代码
gen.MarkLabel(lbTrue); //声明拦截类型变量,存储当前切面特性的拦截类型 InterceptType interceptType = array[i].InterceptType;
LocalBuilder interceptType = gen.DeclareLocal(typeof(InterceptType)); gen.Emit(OpCodes.Ldarg_3);
gen.Emit(OpCodes.Ldloc, locI);
gen.Emit(OpCodes.Ldelem_Ref);
gen.Emit(OpCodes.Callvirt, typeof(AspectAttribute).GetMethod("get_InterceptType"));
gen.Emit(OpCodes.Box, typeof(InterceptType));
gen.Emit(OpCodes.Stloc, interceptType); //if 条件 if (text.Equals("OnEntry")
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ldstr, "OnEntry");
gen.Emit(OpCodes.Call, typeof(String).GetMethod("Equals", new Type[] { typeof(string) }));
gen.Emit(OpCodes.Brfalse, lbIfEntryRet); //if 条件 && interceptType.HasFlag(InterceptType.OnEntry))
gen.Emit(OpCodes.Ldloc, interceptType);
gen.Emit(OpCodes.Ldc_I4, );
gen.Emit(OpCodes.Box, typeof(InterceptType));
gen.Emit(OpCodes.Call, typeof(Enum).GetMethod("HasFlag"));
gen.Emit(OpCodes.Brfalse, lbIfEntryRet); //True
gen.Emit(OpCodes.Ldarg_3);
gen.Emit(OpCodes.Ldloc, locI);
gen.Emit(OpCodes.Ldelem_Ref);
gen.Emit(OpCodes.Ldarg_2);
gen.Emit(OpCodes.Callvirt, typeof(AspectAttribute).GetMethod("OnEntry")); gen.MarkLabel(lbIfEntryRet); //if 条件 if (text.Equals("OnExit"))
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ldstr, "OnExit");
gen.Emit(OpCodes.Call, typeof(String).GetMethod("Equals", new Type[] { typeof(string) }));
gen.Emit(OpCodes.Brfalse, lbIfExitRet); //if 条件 (&& interceptType.HasFlag(InterceptType.OnExit))
gen.Emit(OpCodes.Ldloc, interceptType);
gen.Emit(OpCodes.Ldc_I4, );
gen.Emit(OpCodes.Box, typeof(InterceptType));
gen.Emit(OpCodes.Call, typeof(Enum).GetMethod("HasFlag"));
gen.Emit(OpCodes.Brfalse, lbIfExitRet); //True
gen.Emit(OpCodes.Ldarg_3);
gen.Emit(OpCodes.Ldloc, locI);
gen.Emit(OpCodes.Ldelem_Ref);
gen.Emit(OpCodes.Ldarg_2);
gen.Emit(OpCodes.Callvirt, typeof(AspectAttribute).GetMethod("OnExit")); gen.MarkLabel(lbIfExitRet); //追加代码 //i++
gen.Emit(OpCodes.Ldloc, locI);
gen.Emit(OpCodes.Ldc_I4_1);
gen.Emit(OpCodes.Add);
gen.Emit(OpCodes.Stloc, locI); //判断代码
gen.MarkLabel(lbCondition);
gen.Emit(OpCodes.Ldloc, locI);
gen.Emit(OpCodes.Ldarg_3);
gen.Emit(OpCodes.Ldlen);
gen.Emit(OpCodes.Conv_I4); gen.Emit(OpCodes.Clt);
//如果True,跳至true代码
gen.Emit(OpCodes.Brtrue, lbTrue); //********
gen.Emit(OpCodes.Nop); gen.Emit(OpCodes.Ret); return result;
}
#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 = , /// <summary>
/// 方法
/// </summary>
Method = , /// <summary>
/// 属性的Set方法
/// </summary>
Setter = , /// <summary>
/// 属性的Get方法
/// </summary>
Getter = , /// <summary>
/// 所有
/// </summary>
All = #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:
/// <summary>
/// 重写方法
/// </summary>
public class ILOverrideMethod
{
#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="srcMethod">源方法</param>
/// <param name="overrideIL">override方法IL</param>
public ILOverrideMethod(MethodInfo srcMethod, ILGenerator overrideIL)
{
SrcMethod = srcMethod; OverrideMethodIL = overrideIL; ParamTypes = XDynamic.GetMethodParameterTypes(SrcMethod);
}
#endregion #region 属性 /// <summary>
/// 源方法
/// </summary>
public MethodInfo SrcMethod { get; set; } /// <summary>
/// 重写方法IL
/// </summary>
public ILGenerator OverrideMethodIL { get; set; } #endregion #region Protected /// <summary>
/// 返回值
/// </summary>
protected LocalBuilder Result; /// <summary>
/// 方法参数列表
/// </summary>
protected Type[] ParamTypes; #endregion #region 生成重写方法 Create
/// <summary>
/// 生成重写方法
/// </summary>
public virtual void Create()
{
CreateMethodBody();
57
58 GetResult();
59
60 Return();
}
#endregion #region 创建方法体 CreateMethodBody
/// <summary>
/// 创建方法体
/// </summary>
protected virtual void CreateMethodBody()
{
//类对象,参数值依次入栈并调用基类的方法
for (int i = ; i <= ParamTypes.Length; i++)
OverrideMethodIL.Emit(OpCodes.Ldarg, i); OverrideMethodIL.Emit(OpCodes.Call, SrcMethod);
}
#endregion #region 获取结果 GetResult
/// <summary>
/// 获取结果
/// </summary>
protected virtual void GetResult()
{
//如果有返回值,保存返回值到局部变量
if (SrcMethod.ReturnType != typeof(void))
{
Result = OverrideMethodIL.DeclareLocal(SrcMethod.ReturnType);
OverrideMethodIL.Emit(OpCodes.Stloc, Result);
}
}
#endregion #region 返回 Return
/// <summary>
/// 返回
/// </summary>
protected virtual void Return()
{
//如果有返回值,则把返回值压栈
if (Result != null)
OverrideMethodIL.Emit(OpCodes.Ldloc, Result); OverrideMethodIL.Emit(OpCodes.Ret);//返回
}
#endregion
}
ILProxyMethod:
/// <summary>
/// 代理方法
/// </summary>
public class ILProxyMethod : ILOverrideMethod
{
#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="srcMethod">源方法</param>
/// <param name="il"></param>
/// <param name="aspectAttributes">切面特性集合</param>
/// <param name="onEntryExit">切面入口出口统一调用方法</param>
public ILProxyMethod(MethodInfo srcMethod, ILGenerator il, object[] aspectAttributes, MethodInfo onEntryExit)
: base(srcMethod, il)
{
OnEntryExit = onEntryExit; AspectExceptionAttributes = aspectAttributes.Where(p => p.GetType() == typeof(AspectExceptionAttribute)).ToArray(); HandleException = AspectExceptionAttributes.Length > ; //生成切面上下文
AspectContext = CreateAspectContext(OverrideMethodIL, SrcMethod.Name, ParamTypes);
//生成除去异常特性外的特性数组
AspectAttributes = CreateAspectAttributeArray(OverrideMethodIL,
aspectAttributes.Where(p => p.GetType() != typeof(AspectExceptionAttribute)).ToArray());
}
#endregion #region Protected /// <summary>
/// 切面入口出口统一调用方法
/// </summary>
protected MethodInfo OnEntryExit { get; set; } /// <summary>
/// 切面上下文
/// </summary>
protected LocalBuilder AspectContext; /// <summary>
/// 除去异常外的切面特性数组
/// </summary>
protected LocalBuilder AspectAttributes { get; set; } /// <summary>
/// 异常特性
/// </summary>
protected object[] AspectExceptionAttributes; /// <summary>
/// 是否处理异常
/// </summary>
protected bool HandleException = false; #endregion #region 创建方法体 CreateMethodBody
/// <summary>
/// 创建方法体
/// </summary>
protected override void CreateMethodBody()
{
if (HandleException)
OverrideMethodIL.BeginExceptionBlock(); //调用横切对象的OnEntryt方法
CallOn_Entry_Exit(OverrideMethodIL, true, AspectContext, AspectAttributes, OnEntryExit); base.CreateMethodBody();
}
#endregion #region 获取结果 GetResult
/// <summary>
/// 获取结果
/// </summary>
protected override void GetResult()
{
base.GetResult(); if (SrcMethod.ReturnType != typeof(void))
{
//给AspectContext的属性Result赋值
var resultSetMethod = typeof(AspectContext).GetMethod("set_Result");
OverrideMethodIL.Emit(OpCodes.Ldloc, AspectContext); //加载AspectContext局部变量
OverrideMethodIL.Emit(OpCodes.Ldloc, Result);//加载返回值
OverrideMethodIL.Emit(OpCodes.Box, SrcMethod.ReturnType);
OverrideMethodIL.Emit(OpCodes.Call, resultSetMethod);//赋值
}
}
#endregion #region 返回
/// <summary>
///
/// </summary>
protected override void Return()
{
//调用横切对象的OnExit方法
CallOn_Entry_Exit(OverrideMethodIL, false, AspectContext, AspectAttributes, OnEntryExit); if (HandleException)
{
AspectExceptionAttribute temp = AspectExceptionAttributes[] as AspectExceptionAttribute; OverrideMethodIL.BeginCatchBlock(typeof(Exception)); //保存Exception到临时变量
LocalBuilder exception = OverrideMethodIL.DeclareLocal(typeof(Exception)); OverrideMethodIL.Emit(OpCodes.Stloc, exception); //复制Exception到Result
var resultSetMethod = typeof(AspectContext).GetMethod("set_Result");
OverrideMethodIL.Emit(OpCodes.Ldloc, AspectContext); //加载AspectContext局部变量
OverrideMethodIL.Emit(OpCodes.Ldloc, exception);//错误信息
OverrideMethodIL.Emit(OpCodes.Box, typeof(Exception));
OverrideMethodIL.Emit(OpCodes.Call, resultSetMethod);//赋值 //调用OnEntry
LocalBuilder array = CreateAspectAttributeArray(OverrideMethodIL, AspectExceptionAttributes); CallOn_Entry_Exit(OverrideMethodIL, true, AspectContext, array, OnEntryExit); //触发Finally
if (temp.InterceptType.HasFlag(InterceptType.OnExit))
{
OverrideMethodIL.BeginFinallyBlock(); //调用Exit
array = CreateAspectAttributeArray(OverrideMethodIL, AspectExceptionAttributes); CallOn_Entry_Exit(OverrideMethodIL, false, AspectContext, array, OnEntryExit);
} OverrideMethodIL.EndExceptionBlock();
} base.Return();
}
#endregion
}
通过上边的代码我们可以看到,ILGenerateProxyMethod就拆分为一个类的三个方法,同时也灵活的处理了异常特性(if (HandleException),这里边新增了一个CallOn_Entry_Exit方法,其对应代码如下:
private static void CallOn_Entry_Exit(ILGenerator il_ProxyMethod, bool entry, LocalBuilder aspectContext, LocalBuilder array, MethodInfo onEntryExit)
{
il_ProxyMethod.Emit(OpCodes.Ldarg_0);
il_ProxyMethod.Emit(OpCodes.Ldstr, entry ? "OnEntry" : "OnExit");
il_ProxyMethod.Emit(OpCodes.Ldloc, aspectContext);
il_ProxyMethod.Emit(OpCodes.Ldloc, array); il_ProxyMethod.Emit(OpCodes.Call, onEntryExit);
}
#endregion
测试
测试类修改如下:
public class AopTest
{ public AopTest()
{
Name = "小明"; Age = ;
} 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() Log OnEntry:NYearLater()
Log OnExit: NYearLater Result:
Finally : NYearLater done...
比对结果,达到了我们的目的。