近日在学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
{
//业务层的类和方法,让他继承自上下文绑定类的基类
[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上,右击,就会出现
实现接口,点击
实现接口即可.下图:
自动生成的异步调用方法,在这里我们没用到,后面处理就会发现,并没有执行这条语句。
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.执行结果如下:
很显然得到了验证.读者可以自行去掉继承的那个类或者去掉标签,再看看效果.
到此,该项目就结束了.如果读者不知道程序怎么调用的?那就自己单步调试吧.更容易帮助你理解.
最后附上源码, 源码下载