.net利用Attribute简单实现AOP

近日在学AOP,前一篇文章是基于透明代理/真实代理实现的,(前文参考链接:http://blog.csdn.net/jiujiu28/article/details/43562909),但是每次实现AOP之前需要实例化一个新对象,总感觉不爽.这不.接下来我们使用Attribute来实现AOP.

一、什么是Attribute?

Attribute是一个类,在 MSDN文档中对它是这样描述的。
Attribute 类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。 目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性、返回值、结构或其他特性。所有特性类型都直接或间接地从 Attribute 类派生。 特性可应用于任何目标元素;多个特性可应用于同一目标元素;并且特性可由从目标元素派生的元素继承。 使用 AttributeTargets 类可以指定特性所应用到的目标元素。MSDN原文请参考: https://msdn.microsoft.com/zh-cn/library/system.attribute(v=vs.110).aspx
官方的话还是太官方了,我用通俗一点的话来说就是,用Attribute属性给相应的类或方法贴上标签,在调用的时候,根据标签来判断是否执行一些自定义内容.(如果说的有误,请读者批评指正,先感谢了).
由于对Attribute运用的不多,对它的理解也没有那么透彻,如果读者很想弄清楚,推荐你看看这个,对你理解Attribute应该会有帮助.参考链接: http://www.cnblogs.com/clhed/articles/1324175.html

二、下面我们就用一个.net实例,来看看怎么使用Attribute来实现AOP吧.这个实例实现的计算器简单运算.

先看看整个项目的结构,下图: (后附上源码)
.net利用Attribute简单实现AOP_第1张图片
1.首先我们打开VS2010,新建一个解决方案,名字随便起,小编命名为:AopAttributePractice.
(在这小编啰嗦一句:给项目起名别太任性,最好能见文知义,最好别用拼音,别告诉我不知道单词怎么写,在线翻译工具那么多,别偷懒啊)
2.为AopAttributePractice解决方案添加一个C#类库,命名为:Calculator.
3.为Calculator类库添加一个Calculator类,类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Calculator
{
    public class Calculator
    {
        private double _x;
        private double _y;

        public Calculator()
        { }

        public Calculator(double x, double y)
        {
            this._x = x;
            this._y = y;
        }

        public double X
        {
            get { return _x; }
            set { _x = value; }
        }

        public double Y
        {
            get { return _y; }
            set { _y = value; }
        }
    }
}
代码浅显易懂,就不解释了.
2.为Calculator类库添加一个类,命名为ICalculator,此类目的是在定义一个接口,类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Calculator
{
    public interface ICalculator
    {
        double Add(double x, double y);

        double substract(Calculator calculator);

        double Mutiply(double x, double y);

        double Divide(Calculator calculator);
    }
}
方法从上到下依次为:加法,减法,乘法,除法。
3.为Calculator类库添加一个实现ICalculator的类,命名为:CalculatorHandler,类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Calculator
{
    //业务层的类和方法,让他继承自上下文绑定类的基类
    [MyCalculator]
    public class CalculatorHandler:ContextBoundObject,ICalculator
    {
        //具备标签的方法才能被拦截
        [MyCalculatorMethod]
        public double Add(double x, double y)
        {
            Console.WriteLine("{0} + {1} = {2}", x, y, x + y);
            return x + y;
        }

        [MyCalculatorMethod]
        public double substract(Calculator calculator)
        {
            Console.WriteLine("{0} - {1} = {2}",calculator.X,calculator.Y,calculator.X - calculator.Y);
            return calculator.X - calculator.Y;
        }

        public double Mutiply(double x, double y)
        {
            Console.WriteLine("{0} * {1} = {2}", x, y, x * y);
            return x * y;
        }

        public double Divide(Calculator calculator)
        {
            Console.WriteLine("{0} / {1} = {2}", calculator.X, calculator.Y, calculator.X / calculator.Y);
            return calculator.X / calculator.Y;
        }
    }
}
让该类继承 ContextBoundObject类和ICalculator类.ICalulator类不做解释,下面对ContextBoundObject类做简单的解释:
ContextBoundObject类:实现被拦截的类,需要从ContextBoundObject类派生,这个类的对象通过Attribute来指定它所在Context,凡是进入该Context的调用都可以被拦截.
代码中红色部分需要基于后面代码的实现才能实现,在这一步,可以先不用理会。
4.准备工作做好了接下来就开始Attribute的实现过程。
新建类MyCalculatorAttribute,类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Runtime.Remoting.Contexts;

namespace Calculator
{
    //贴上类的标签
    [AttributeUsage(AttributeTargets.Class,AllowMultiple = false)]
    //当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承
    //ContextAttribute:构造器带有一个参数,用来设置ContextAttribute的名称。
    public sealed class MyCalculatorAttribute:ContextAttribute,
        IContributeObjectSink
    {

        public MyCalculatorAttribute()
            : base("MyCalculator")
        {
            Console.WriteLine("MyCalculatorAttribute...构造函数");
        }


        //实现IContributeObjectSink接口当中的消息接收器接口
        public System.Runtime.Remoting.Messaging.IMessageSink GetObjectSink(MarshalByRefObject obj, System.Runtime.Remoting.Messaging.IMessageSink nextSink)
        {
            return new MyAopHandler(nextSink);
        }
    }
}
重要注释都在类中标明,再次不再累述.
5.继续添加新类MyCalculatorMethodAttribute类,类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Calculator
{
    //贴上方法标签
    [AttributeUsage(AttributeTargets.Method,AllowMultiple = false)]
    public class MyCalculatorMethodAttribute:Attribute
    {
        public MyCalculatorMethodAttribute()
        {
            Console.WriteLine("MyCalculatorMethodAttribute...构造函数");
        }

    }
}
6.下面就开始我们的拦截过程,也就是实现AOP的重要部分.
新建一个MyAopHandler类,类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Runtime.Remoting.Messaging;

namespace Calculator
{
    //AOP方法处理类,实现了IMessageSink接口,以便返回给IContributeObjectSink接口的GetObjectSink方法
    public sealed class MyAopHandler:IMessageSink
    {
       
        //下一个接收器
        private IMessageSink nextSink;

        public MyAopHandler(IMessageSink nextSink)
        {
            this.nextSink = nextSink;
        }

        public IMessageSink NextSink
        {
            get { return nextSink; }
        }

        //同步处理方法
        public IMessage SyncProcessMessage(IMessage msg)
        {

            IMessage message = null;

            //方法调用接口
            IMethodCallMessage callMessage = msg as IMethodCallMessage;

            //如果被调用的方法没打MyCalculatorMethodAttribute标签
            if (callMessage == null || (Attribute.GetCustomAttribute(callMessage.MethodBase, typeof(MyCalculatorMethodAttribute))) == null)
            {
                message = nextSink.SyncProcessMessage(msg);
            }
            else
            {
                PreProceed(msg);
                message = nextSink.SyncProcessMessage(msg);
                PostProceed(message);
            }

            return message;
        }

        //异步处理方法
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            Console.WriteLine("异步处理方法...");
            return null;
        }

        //方法执行前
        public void PreProceed(IMessage msg)
        {
            IMethodMessage message = (IMethodMessage)msg;
            Console.WriteLine("New Method Start...");
            Console.WriteLine("This Method Is {0}", message.MethodName);
            Console.WriteLine("This Method A Total of {0} Parameters Including:", message.ArgCount);
            for (int i = 0; i < message.ArgCount; i++)
            {
                Console.WriteLine("Parameter{0}:The Args Is {1}.",i+1,message.Args[i]);
            }
        }

        //方法执行后
        public void PostProceed(IMessage msg)
        {
            IMethodReturnMessage message = (IMethodReturnMessage)msg;

            Console.WriteLine("The Return Value Of This Method Is {0}", message.ReturnValue);
            Console.WriteLine("Method End\n");
        }
    }
}
图中的方法不需要我们自己逐个写,只需要把鼠标放在继承的类IMessageSink上,右击,就会出现 实现接口,点击 实现接口即可.下图:
.net利用Attribute简单实现AOP_第2张图片
自动生成的异步调用方法,在这里我们没用到,后面处理就会发现,并没有执行这条语句。
7.OK,Calculator类库中的代码就这么多了 ,别忘了给CalculatorHandler类中方法添加相应的标签.
接下来我们就实现这个AOP拦截过程。
在解决方案里新建一个控制台应用程序(ConsoleApplication).命名为:AopAttributeCalculatorClient,Program类中代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Calculator;
using System.Runtime.Remoting.Contexts;

namespace AopAttributeCalculatorClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //实例化Calculator类
            Calculator.Calculator calculator = new Calculator.Calculator();
            calculator.X = 10;
            calculator.Y = 20;

            CalculatorHandler handler = new CalculatorHandler();
            //打上标签的方法
            handler.Add(calculator.X, calculator.Y);
            handler.substract(calculator);
            //没有打标签的方法
            handler.Mutiply(30, 40);//自定义传值,跟Calculator类无关
            handler.Divide(calculator);
        }      
    }
8.来看看控制台输出情况吧,下图:
.net利用Attribute简单实现AOP_第3张图片
添加标签的方法都被遭到了拦截,显然达到了的我们的目的。
9.到此,小编有个疑问,是不是在此基础上,定义类的时候,给类和方法添加标签都能被拦截呢??很明显是肯定可以被拦截的.不信??下面这个例子会让你相信的。
我们在Program类中添加一些代码,Program中整个代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Calculator;
using System.Runtime.Remoting.Contexts;

namespace AopAttributeCalculatorClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //实例化Calculator类
            Calculator.Calculator calculator = new Calculator.Calculator();
            calculator.X = 10;
            calculator.Y = 20;

            CalculatorHandler handler = new CalculatorHandler();
            //贴上标签的方法
            handler.Add(calculator.X, calculator.Y);
            handler.substract(calculator);
            //为贴上标签的方法
            handler.Mutiply(30, 40);
            handler.Divide(calculator);

            //实例化Communition类
            Communication com = new Communication();
            com.SayBye();
            com.SayHello();

        }      
    }

    //贴上类的标签
    [MyCalculator]
    //一定要继承ContextBoundObject类
    public class Communication:ContextBoundObject
    {
        public Communication()
        { }
        //贴上方法标签
        [MyCalculatorMethod]
        public void SayHello()
        {
            Console.WriteLine("hello...");
        }

        public void SayBye()
        {
            Console.WriteLine("bye!");
        }
    }

}
10.执行结果如下:
.net利用Attribute简单实现AOP_第4张图片
很显然得到了验证.读者可以自行去掉继承的那个类或者去掉标签,再看看效果.
到此,该项目就结束了.如果读者不知道程序怎么调用的?那就自己单步调试吧.更容易帮助你理解.
最后附上源码, 源码下载










你可能感兴趣的:(Attribute,.net,AOP)