C#当中利用Attribute实现简易AOP

Posted Jason.Zeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#当中利用Attribute实现简易AOP相关的知识,希望对你有一定的参考价值。

首先看一段简单的代码:

复制代码
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    //来自UI层的调用
    private void button1_Click(object sender, EventArgs e)
    {
        BusinessHandler handler = new BusinessHandler();
        handler.DoSomething();
    }
}

//业务层的类和方法
public class BusinessHandler
{
    public void DoSomething()
    {
        MessageBox.Show("执行了方法本身!");
    }
}
复制代码

那么假设这是一个web程序,我们想在按钮点击时,先校验session,然后检验权限、起事务、写日志,然后再执行操作本身,最后再写日志。显然不能把对上面这些操作的方法调用都写在DoSomething()里,这样代码会变成一大坨屎,那我们该怎么办呢?

所谓AOP(面向切面编程)这个唬人的名词就是干这件事用的,其实现方式有很多种,比如利用spring等框架,但是在实际项目中并不是想引一个框架进来就能随便引的,很多时候都需要我们自己手写一些机制。

这里想到了MVC当中的Filter,只要在Controller或者Action上打一个特性标签(Attribute),就能在方法执行前后做一些其他事情了。那么我们就来简单模拟一个Filter的实现吧。

首先给原先的方法改造一下,改成特性标签这种优雅的方式:

复制代码
//业务层的类和方法,让它继承自上下文绑定类的基类
[MyInterceptor]
public class BusinessHandler : ContextBoundObject
{
    [MyInterceptorMethod]
    public void DoSomething()
    {
        MessageBox.Show("执行了方法本身!");
    }
}
复制代码

补充: .NET上下文(ContextBoundObject对象)

什么叫上下文,千万别和ASP.NET中的上下文搞混了,这个上下文是个形容词,在不同的场合有不同的意思。在ASP.NET中的上下文是指Context对象,这个对象基本上包容了HTTP协议的整个生命周期的信息,可以获取到客户端浏览器的一些基本信息,也可以获取到关于HTTP协议的一些信息,等等。

这里所讲的上下文是.NET程序执行的最小逻辑范围,ASP.NET上下文是站在B/S编程模型角度去看待的,而这里的上下文是站在.NET底层运行角度看来的,后者是代码的上下文,前者是整个生命周期的上下文。

在没有接触ContextBoundObject之前我一直以为.NET程序执行的最小逻辑范围是应用程序域(AppDomain),知道了之后才知道另有隐情,上下文是用来确定对象的逻辑归属,在多线程(Thread)、事物处理(Transaction)、企业服务(Enterprise)等方面都需要用上下文来对对象进行规划。

有了特性标签自然就要有特性标签对应的类,以及AOP的实现方法,这些东西可以单独独立到一个文件或程序集里。

首先是贴在方法上的标签,实现为空:

//贴在方法上的标签[MyInterceptorMethod]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class MyInterceptorMethodAttribute : Attribute
{ }

之后定义贴在类上的标签:

复制代码
//贴在类上的标签
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MyInterceptorAttribute : ContextAttribute, IContributeObjectSink
{
    public MyInterceptorAttribute()
        : base("MyInterceptor")
    { }

    //实现IContributeObjectSink接口当中的消息接收器接口
    public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
    {
        return new MyAopHandler(next);
    }
}
复制代码

补充: 这里继承了ContextAttribute, 在.net中怎么使用attribute,参考: http://blog.csdn.net/u013473447/article/details/41785823

这里定义了一个MyAopHandler类,实现如下:

复制代码
//AOP方法处理类,实现了IMessageSink接口,以便返回给IContributeObjectSink接口的GetObjectSink方法
public sealed class MyAopHandler : IMessageSink
{
    //下一个接收器
    private IMessageSink nextSink;
    public IMessageSink NextSink
    {
        get { return nextSink; }
    }
    public MyAopHandler(IMessageSink nextSink)
    {
        this.nextSink = nextSink;
    }

    //同步处理方法
    public IMessage SyncProcessMessage(IMessage msg)
    {
        IMessage retMsg = null;

        //方法调用消息接口
        IMethodCallMessage call = msg as IMethodCallMessage;

        //如果被调用的方法没打MyInterceptorMethodAttribute标签
        if (call == null || (Attribute.GetCustomAttribute(call.MethodBase, typeof(MyInterceptorMethodAttribute))) == null)
        {
            retMsg = nextSink.SyncProcessMessage(msg);
        }
        //如果打了MyInterceptorMethodAttribute标签
        else
        {
            MessageBox.Show("执行之前");
            retMsg = nextSink.SyncProcessMessage(msg);
            MessageBox.Show("执行之后");
        }

        return retMsg;
    }

    //异步处理方法(不需要)
    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
    {
        return null;
    }
}
复制代码

注意到上面的执行前后具体操作部分代码,相当于一个“代理类”,它实质上是改变了方法执行的上下文。可以用委托等面向对象程序结构把具体实现暴露给外部进行二次开发。

以上是关于C#当中利用Attribute实现简易AOP的主要内容,如果未能解决你的问题,请参考以下文章

手写实现自定义简易版Spring (实现IoC 和 AOP)

手写实现自定义简易版Spring (实现IoC 和 AOP)

C#实现简易ajax调用后台方法

如何实现一个简易版的 Spring

Spring Boot使用AOP实现REST接口简易灵活的安全认证

使用AOP 使C#代码更清晰