.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吧.這個實例實現的計算器簡單運算.

先看看整個項目的結構,下圖:(後附上源碼)
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.執行結果如下:

很顯然得到了驗證.讀者可以自行去掉繼承的那個類或者去掉標籤,再看看效果.
到此,該項目就結束了.如果讀者不知道程序怎麼調用的?那就自己單步調試吧.更容易幫助你理解.
最後附上源碼,源碼下載










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章