.net利用Attribute简单实现AOP

来源:互联网 发布:云数据加密传输运营商 编辑:程序博客网 时间:2024/05/22 14:14

近日在学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吧.这个实例实现的计算器简单运算.

先看看整个项目的结构,下图:(后附上源码)
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{    //业务层的类和方法,让他继承自上下文绑定类的基类    <span style="color:#ff0000;">[MyCalculator]</span>    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;        }        <span style="color:#ff0000;">[MyCalculatorMethod]</span>        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上,右击,就会出现实现接口,点击实现接口即可.下图:

自动生成的异步调用方法,在这里我们没用到,后面处理就会发现,并没有执行这条语句。
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.来看看控制台输出情况吧,下图:

添加标签的方法都被遭到了拦截,显然达到了的我们的目的。
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.执行结果如下:

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










0 0
原创粉丝点击