一、什麼是Attribute?
Attribute是一個類,在MSDN文檔中對它是這樣描述的。
官方的話還是太官方了,我用通俗一點的話來說就是,用Attribute屬性給相應的類或方法貼上標籤,在調用的時候,根據標籤來判斷是否執行一些自定義內容.(如果說的有誤,請讀者批評指正,先感謝了).
二、下面我們就用一個.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.執行結果如下:
很顯然得到了驗證.讀者可以自行去掉繼承的那個類或者去掉標籤,再看看效果.
到此,該項目就結束了.如果讀者不知道程序怎麼調用的?那就自己單步調試吧.更容易幫助你理解.