PostSharp AOP教程

AOP(基于切面编程):它是对业务逻辑的分离,使各个业务直接的耦合变低,比如在传统的OOP编程中将日志记录、异常处理、权限管理等方面剥离出来。在今后的维护过程中,对其改变日志记录、异常处理、权限管理方法的时候,不用去改变主业务流程逻辑代码。提高开发效率。

        PostSharp采用特性的方式来对编译后的主业务流程方法逻辑横向静态注入截取数据。下面我们将以一个实例来演示如何使用PostSharp实现AOP进行日志记录和异常处理。

        首先需要安装PostSharp 2.1.4.1免费版本,这个版本只是功能相对较少,可商用,能够满足日志记录和异常截取的要求。点击PostSharp-2.1.4.1 下载.

        PostSharp AOP教程_第1张图片

        然后新建一个控制台项目并将PostSharp.dll引入该项目中。编写一个截取日志的特性LogsAttributes.cs。

复制代码
    //日志特性截取类
[Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
class LogsAttribute:OnMethodBoundaryAspect
{
///<summary>
/// 入口参数信息
///</summary>
public string EntryText { get; set; }

///<summary>
/// 出口参数信息
///</summary>
public string ExitText { get; set; }

///<summary>
/// 异常信息
///</summary>
public string ExceptionText { get; set; }

//进入函数时输出函数的输入参数
public override void OnEntry(MethodExecutionArgs eventArgs)
{
Arguments arguments = eventArgs.Arguments;
StringBuilder sb = new StringBuilder();
ParameterInfo[] parameters = eventArgs.Method.GetParameters();
for (int i = 0; arguments != null && i < arguments.Count; i++)
{
//进入的参数的值
sb.Append( parameters[i].Name + "=" + arguments[i] + "");
}
string message = string.Format("{0}.{1} Method. The Entry Arg Is:{2}",
eventArgs.Method.DeclaringType.FullName, eventArgs.Method.Name, sb.ToString());
Console.WriteLine(message);
}

//退出函数时的函数返回值
public override void OnExit(MethodExecutionArgs eventArgs)
{
Console.WriteLine(string.Format("{0}.{1} Method. The Result Is:{2}",
eventArgs.Method.DeclaringType.FullName, eventArgs.Method.Name, eventArgs.ReturnValue.ToString()));
}

//函数发生异常时记录异常信息
public override void OnException(MethodExecutionArgs eventArgs)
{
Console.WriteLine(string.Format("{0}.{1} Method. The Exception Is:{2}",
eventArgs.Method.DeclaringType.FullName, eventArgs.Method.Name, eventArgs.Exception.Message));

}
}
复制代码

        再看看当出现异常时通过try{} catch{}处理异常,ExceptionAttribute.cs类捕捉并且处理异常:

复制代码
    //截取异常并且处理异常
[Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
class ExceptionAttribute:MethodInterceptionAspect
{
//调用本函数时截取异常
public override void OnInvoke(MethodInterceptionArgs args)
{
try
{
base.OnInvoke(args);
}
catch(Exception ex)
{
Console.WriteLine(string.Format("此方法异常信息是:{0}", ex.ToString()));
}
}
}
复制代码

        在客户端编写两个方法来测试PostSharp是否静态注入并且捕获到相关信息,代码如下:

复制代码
    class Program
{
static void Main(string[] args)
{
Add(3, 5);
Console.WriteLine("-------------------------------------------------------");
Subject(5, 12);
Console.ReadLine();
}

//此函数让我们看其输入参数和返回值的日志记录
[Logs]
[Exception]
public static int Add(int a, int b)
{
return a + b;
}

//此函数看我们的异常通过自定义Exception特性记录下来
[Logs]
[Exception]
public static int Subject(int a, int b)
{

throw new ArgumentException("减法出现异常,需要处理");

return a - b;
}
}
复制代码

        下面是项目运行效果,如需源码请点击 PostSharpAOP.zip 下载。

PostSharp AOP教程_第2张图片

PostSharp是编译时将IL语言静态注入程序中,在免费版本中我们能够使用PostSharp对方法、字段和属性进行AOP截取数据而不能对事件进行AOP截取。下面我们来看看针对方法、字段和属性的AOP处理类:

     OnMethodBoundaryAspect

     继承于此类的特性A将围绕整个方法B进行静态注入式的处理,这个特性A可以重载覆写OnMethodBoundaryAspect类的OnEntry()、OnSuccess()、OnException()、OnExit()方法。如下代码:

    [Serializable]
public sealed class LogsAttribute : OnMethodBoundaryAspect
{
//进入函数时
public override void OnEntry(MethodExecutionArgs args)
{
base.OnEntry(args);
}
//函数执行成功时
public override void OnSuccess(MethodExecutionArgs args)
{
base.OnSuccess(args);
}
//发生异常时
public override void OnException(MethodExecutionArgs args)
{
base.OnException(args);
}
//退出函数时
public override void OnExit(MethodExecutionArgs args)
{
base.OnExit(args);
}
}

       其覆写函数时的运行顺序和结果如下代码所示:

        int MethodA(object arg0, int arg1)
{
//函数进入时添加OnEntry()方法
OnEntry();
try
{
// 函数在此运行完毕
//如果成功则继续运行OnSuccess()方法
OnSuccess();
return returnValue;
}
catch (Exception e)
{
//如果函数执行有问题,则运行OnException()方法
OnException();
}
finally
{
//最后执行OnExit()方法
OnExit();
}
}

      所以我们可以通过OnMethodBoundaryAspect编写特性去围绕方法截取数据,并且处理方法内部数据。如异常处理特性、日志记录特性、缓存记录特性和权限管理特性。

 在PostSharp中得LocationInterceptionAspect类是针对属性或字段的面向方面截取。继承与它的特性将可以截取初始化属性、设置属性、获取属性等时候的数据,并且可以在这几个过程中针对属性进行附加控制。

        这个类里面有三个主要的函数可以重载分别是RuntimeInitialize(LocationInfo locationInfo)、OnSetValue(LocationInterceptionArgs args)、OnGetValue(LocationInterceptionArgs args)。他们分别意义如下:

        RuntimeInitialize(LocationInfo locationInfo):初始化包含属性或字段的类的时候运行此函数,增加控制代码,可以截取到运行此属性或字段的类信息,属性类型等信息。

        OnSetValue(LocationInterceptionArgs args):设置属性或字段值的时候运行此函数,增加相关设置值时代码,可以获取到此属性值、属性名等相关信息。

        OnGetValue(LocationInterceptionArgs args)。获取属性或字段值的时候运行此函数。

        首先我们编写一个继承于LocationInterceptionAspect类的特性,并且重载相关函数如下代码:

    [Serializable]
public class TestAspect : LocationInterceptionAspect
{
//当目标类初始化属性的时候运行此函数
public override void RuntimeInitialize(LocationInfo locationInfo)
{
//打印类名、属性或字段名、字段类型
string name = locationInfo.DeclaringType.Name + "." +
locationInfo.Name + " (" + locationInfo.LocationType.Name + ")"; ;
Console.WriteLine(name);
Console.WriteLine("A");
System.Reflection.FieldInfo finfo = locationInfo.FieldInfo;
}
//设置属性的时候运行
public override void OnSetValue(LocationInterceptionArgs args)
{
Console.WriteLine(args.LocationName);
Console.WriteLine("B");
base.OnSetValue(args);
}
//获取属性的时候运行
public override void OnGetValue(LocationInterceptionArgs args)
{
Console.WriteLine("C");
base.OnGetValue(args);
}
}

        其次我们编写一个目标类,此类中含有一个属性并且增加这个属性的特性如下代码所示:

    public class People
{
[TestAspect]
public string Name { get; set; }
}

        最后我们在客户端初始化People类并且设置属性和获取属性如下代码:

    class Program
{
static void Main(string[] args)
{
People p = new People();
p.Name = "Mike1";
Console.WriteLine(p.Name);
Console.ReadLine();
}

}

        如需源码请点击 PostSharpField.rar  下载,运行效果如下图:

PostSharp AOP教程_第3张图片

 在PostSharp中得OnExceptionAspect类是针对异常的消息截取。继承于它的特性将可以在发生异常的时候对方法体内的异常截取,并且做出动作,看是否停止本程序运行,还是忽略异常。

        这个类里面有两个主要的函数可以重载分别是OnException(MethodExecutionArgs args)、GetExceptionType(System.Reflection.MethodBase targetMethod)。他们分别意义如下:

        OnException(MethodExecutionArgs args):当发生异常时截取异常发生的位置(在哪个命名空间?哪个类?哪个方法?)、异常类型、异常消息等信息,并且可以异常将如何处理。

        GetExceptionType(System.Reflection.MethodBase targetMethod):设置需要拦截的异常类型,比如设置需要拦截参数异常,那么其他的异常类型发生时将不会被此特性所拦截。

        首先我们编写一个继承于OnExceptionAspect类的特性,并且重载相关函数如下代码:

    [Serializable]
public class ExceptionAttribute : OnExceptionAspect
{
//当异常发生时
public override void OnException(MethodExecutionArgs args)
{
Console.WriteLine("______________________________________________________________________________");
Console.WriteLine("异常时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); ;
Console.WriteLine("异常类名:" + args.Method.DeclaringType.FullName);
Console.WriteLine("异常方法:" + args.Method.Name);
Console.WriteLine("异常信息:" + args.Exception.ToString());
args.FlowBehavior = FlowBehavior.Continue;
}

//需要拦截的异常类型为ArgumentException
public override Type GetExceptionType(System.Reflection.MethodBase targetMethod)
{
return typeof(ArgumentException);
}
}

        其次我们编写一个目标类,此类中含有一个方法,方法内抛出参数异常,并为其添加Exception的特性,如以下代码所示:

    class People
{
[Exception]
public void Test(int a, int b)
{
throw new ArgumentException("这里有参数错误,请检查参数");
}
}

        再编写一个对比目标类,要实现同样的效果需要远远多于我们使用PostSharp的情况,而且会让我们的逻辑处理更为复杂和麻烦,如下代码所示:

    class Pelple1
{
public void Test(int a, int b)
{
try
{
throw new ArgumentException("这里有参数错误,请检查参数");
}
catch (ArgumentException args)
{
Console.WriteLine("______________________________________________________________________________");
Console.WriteLine("异常时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); ;
Console.WriteLine("异常信息:" + args.ToString());
}
}
}

        最后我们在客户端初始化People类并且引用方法如下代码:

    class Program
{
static void Main(string[] args)
{
People p = new People();
p.Test(3,5);

Pelple1 p1 = new Pelple1();
p1.Test(3, 5);
Console.ReadLine();
}
}

        如需源码请点击 PostSharpOnExceptionAspect.zip  下载,运行效果如下图:

PostSharp AOP教程_第4张图片

PostSharp中的MethodInterceptionAspect类是针对整个方法体的截取,继承于它的特性可以对整个方法体进行控制和日志截取、异步操作等!这个类里面有一个主要的函数可以重载以实现包围整个方法体截取的作用,它是OnInvoke(MethodInterceptionArgs args)。意义如下:

        OnInvoke(MethodInterceptionArgs args):在它的内部可以通过base.OnInvoke(args)来调用我们加特性声明的方法执行流程,通过这个方法我们可以在方法开始调用前做操作,调用之后做操作。

        首先我们编写一个继承于MethodInterceptionAspect类的特性,并且重载相关函数如下代码:

    [Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
class ExceptionLogAttribute:MethodInterceptionAspect
{
//针对整个方法体进行包围调用添加日志和截取异常
public override void OnInvoke(MethodInterceptionArgs args)
{
Arguments arguments = args.Arguments;
StringBuilder sb = new StringBuilder();
ParameterInfo[] parameters = args.Method.GetParameters();
for (int i = 0; arguments != null && i < arguments.Count; i++)
{
//进入的参数的值
sb.Append( parameters[i].Name + "=" + arguments[i] + "");
}

try
{
Console.WriteLine("进入{0}函数,参数是:{1}", args.Method.DeclaringType + args.Method.Name, sb.ToString());
base.OnInvoke(args);
Console.WriteLine("退出{0}函数,返回结果是:{1}",args.Method.DeclaringType+args.Method.Name,args.ReturnValue);
}
catch(Exception ex)
{
Console.WriteLine(string.Format("出现异常,此方法异常信息是:{0}", ex.ToString()));
}
}
}

        其次我们写两个方法做对比,一个方法会发生异常,另外一个方法不会发生异常,并为其添加ExceptionLog的特性,在客户端进行调用对比,如以下代码所示:

    class Program
{
static void Main(string[] args)
{
SetData("First");
Console.WriteLine("---------------------------------------------------------------------------");
GetData("Second");
Console.ReadLine();
}

[ExceptionLog]
public static string SetData(string str)
{

return "已经设置数据";
}

[ExceptionLog]
public static string GetData(string str)
{

throw new ArgumentException("获取数据出现异常,需要处理");
return "已经获取数据";
}
}

        如需源码请点击 PostSharpMethodInterceptionAspect.zip 下载,运行效果如下图:

 

PostSharp AOP教程_第5张图片



你可能感兴趣的:(PostSharp AOP教程)