溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

.NET里簡易實現(xiàn)AOP

發(fā)布時間:2020-07-28 22:27:22 來源:網(wǎng)絡(luò) 閱讀:1250 作者:jinyuan0829 欄目:編程語言

 

.NET里簡易實現(xiàn)AOP

前言

在MVC的過濾器章節(jié)中對于過濾器的使用就是AOP的一個實現(xiàn)了吧,時常在工作學(xué)習(xí)中遇到AOP對于它的運用可以說是很熟練了,就是沒想過如果自己來實現(xiàn)的話是怎么實現(xiàn)的,性子比較犟硬是沒想明白,茶不思飯不想的,主要問題就是卡在了怎么能攔截用戶調(diào)用,如果可以解決了這個問題AOP的實現(xiàn)也就引刃而解了,為此在網(wǎng)上看了一些文章解決了這個問題,在這里和大家分享一下。

 

AOP概述

AOP的作用就是橫切關(guān)注點,然后將分離后的關(guān)注點已面的形式來呈現(xiàn),這是概念性的說法,舉個列子來說明吧。

比如說有個處理過程是計算提交訂單中的貨品總額,然后想在這個過程中執(zhí)行之前記錄一下數(shù)據(jù)或者是執(zhí)行一些必要的操作。

比如說記錄日志,然后是選擇記錄日志的方式,是選擇存本地文件還是存庫,又或者是選擇了存本地文件后選擇數(shù)據(jù)的存儲介質(zhì)(XML格式、文本格式、加密格式、序列化格式等等)。

這只是其中的一個點,比如說還有驗證等等其它一些方面的關(guān)注點。

圖1

.NET里簡易實現(xiàn)AOP

圖2

.NET里簡易實現(xiàn)AOP

從圖1、圖2中我們就可以看出AOP的目的,就是將日志記錄、驗證、性能監(jiān)測這些關(guān)注點從一個執(zhí)行過程中分離了出來,讓彼此不再有關(guān)系以及和計算總額的關(guān)系。在此可以把分離出來的關(guān)注點封裝,已“面”的形式展現(xiàn)出來,這樣的情況下也使得這些“面”可以在其它地方復(fù)用。

 

AOP的實現(xiàn)

1攔截的基礎(chǔ)實現(xiàn)

在前言中說到在.NET中實現(xiàn)AOP技術(shù)難點就是在攔截的那一塊,看到一些大神的文章利用remoting中的管道技術(shù)來實現(xiàn)信息攔截,我們先來了解一下怎么利用remoting來實現(xiàn)攔截的。

圖3

.NET里簡易實現(xiàn)AOP

圖3就是大概示意出了利用remoting攔截信息的一個示意圖,很突兀的插了個圖可能不太好理解,我們還是通過代碼配合圖文來粗淺的解說一下吧。

代碼1-1

    [AOPWriter]
    public class MyContextObject : ContextBoundObject
    {
        public void WriterLine(string meg)
        {
            Console.WriteLine("這是方法執(zhí)行中" + meg);
        }
    }

代碼1-1中的MyContextObject類型繼承自上下文綁定類ContextBoundObject對象,繼承過后就表示MyContextObject類型需要強制綁定上下文意思就是說它需要在一個特性環(huán)境的上下文中,對于沒有繼承ContextBoundObject類型的類型被稱之為靈活對象,它們的誕生是在默認的當前上下文中。

這里為什么要說到這些內(nèi)容呢?因為這樣才能創(chuàng)建新的上下文,然后當前上下文對于MyContextObject的調(diào)用都是屬于遠程調(diào)用(在remoting里跨越了上下文邊界的所有調(diào)用都應(yīng)該叫遠程調(diào)用,不管服務(wù)端在哪),只有這樣才能利用remoting中的消息管道來進行消息攔截。

那么是在什么時候創(chuàng)建新的上下文的呢?在MyContextObject類型定義的上面,有個AOPWriter特性類型,我們先來看下它的定義,示例代碼1-2.

代碼1-2

    [AttributeUsage(AttributeTargets.Class)]
    public class AOPWriterAttribute : Attribute, IContextAttribute
    {

        public void GetPropertiesForNewContext(IConstructionCallMessage msg)
        {
            msg.ContextProperties.Add(new AOPContextProperty());
            
        }

        public bool IsContextOK(System.Runtime.Remoting.Contexts.Context ctx, IConstructionCallMessage msg)
        {
            return false;
        }
    }

在代碼1-2中我們看到AOPWriterAttribute類型實現(xiàn)了IContextAttribute類型的接口,IContextAttribute類型的定義如下,示例代碼1-3.

代碼1-3

public interface IContextAttribute
    {
        // 摘要:
        //     在給定消息中將上下文屬性返回給調(diào)用方。
        //
        // 參數(shù):
        //   msg:
        //     將上下文屬性添加到的 System.Runtime.Remoting.Activation.IConstructionCallMessage。
        [SecurityCritical]
        void GetPropertiesForNewContext(IConstructionCallMessage msg);
        //
        // 摘要:
        //     返回一個布爾值,指示指定上下文是否滿足上下文屬性的要求。
        //
        // 參數(shù):
        //   ctx:
        //     當前上下文屬性檢查所依據(jù)的上下文。
        //
        //   msg:
        //     構(gòu)造調(diào)用,需要依據(jù)當前上下文檢查其參數(shù)。
        //
        // 返回結(jié)果:
        //     如果傳入的上下文一切正常,則為 true;否則為 false。
        [SecurityCritical]
        bool IsContextOK(Context ctx, IConstructionCallMessage msg);
    }

代碼1-3中的定義同代碼1-2中AOPWriterAttribute類型的實現(xiàn)一樣,首先在上下文綁定對象進行實例化的時候系統(tǒng)默認的會調(diào)用IsContextOK()方法來判斷當前執(zhí)行實例化過程所在的上下文是否滿足于上下文綁定對象的要求,這里我們在代碼1-2中的實現(xiàn)是返回的false,意思就是當前上下文并不滿足于MyContextObject類型所需要的,這是系統(tǒng)會去調(diào)用IContextAttribute中的GetPropertiesForNewContext()方法用于向新建上下文中添加自定義的上下文屬性,也就是實現(xiàn)了IContextProperty接口類型的類型對象,在普通的運用中我們可以在自定義的上下文屬性中設(shè)置一些邏輯操作,以便在新建上下文中使用,對于這部分的示例可以去看我的《.Net組件程序設(shè)計之上下文》篇幅。

對于代碼1-2中AOPContextProperty類型的定義我們也來看一下,示例代碼1-4.

代碼1-4

    /// <summary>
    /// 上下文成員屬性
    /// </summary>
    public class AOPContextProperty : IContextProperty,IContributeServerContextSink
    {

        public void Freeze(Context newContext)
        {

        }

        public bool IsNewContextOK(Context newCtx)
        {
            
            return true;
        }

        public string Name
        {
            get { return "ContextService"; }
        }

        /// <summary>
        /// 提供的服務(wù)
        /// </summary>
        /// <param name="meg"></param>
        public void WriterMessage(string meg)
        {
            Console.WriteLine(meg);
        }

        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            AOPWriterSink megSink = new AOPWriterSink(nextSink);
            return megSink;
        }
}

在代碼1-4中上下文屬性對象添加了個WriterMessage()方法,也就是上面所說的在上下文中的所有對象都可獲取上下文屬性中提供的邏輯操作。Name屬性表示上下文屬性名稱,這個要是唯一的,在獲取上下文屬性就是根據(jù)這個Name屬性值來獲取的,這個下面的消息接收器示例中會有演示。

對了這里上下文屬性還實現(xiàn)了IContributeServerContextSink類型,示例代碼1-5.

代碼1-5

  // 摘要:
    //     在遠程處理調(diào)用的服務(wù)器端上的上下文邊界上分配偵聽接收器。
    [ComVisible(true)]
    public interface IContributeServerContextSink
    {
        // 摘要:
        //     將第一個接收器放入到目前為止組成的接收器鏈中,然后將其消息接收器連接到已經(jīng)形成的鏈前面。
        //
        // 參數(shù):
        //   nextSink:
        //     到目前為止組成的接收鏈。
        //
        // 返回結(jié)果:
        //     復(fù)合接收器鏈。
        [SecurityCritical]
        IMessageSink GetServerContextSink(IMessageSink nextSink);
    }

在上下文屬性的實現(xiàn)中GetServerContextSink()將自定義的消息接收器添加到了新建上下文的消息接收器鏈的前端,這是一點非常重要我們AOP的實現(xiàn)主要依賴于自定義消息接收器中對于調(diào)用函數(shù)信息的攔截。

圖4

.NET里簡易實現(xiàn)AOP

對于消息接收器,是要實現(xiàn)IMessageSink的,代碼1-6.

代碼1-6

   public interface IMessageSink
    {
        // 摘要:
        //     獲取接收器鏈中的下一個消息接收器。
        //
        // 返回結(jié)果:
        //     接收器鏈中的下一個消息接收器。
        //
        // 異常:
        //   System.Security.SecurityException:
        //     直接調(diào)用方通過接口引用進行調(diào)用,它沒有基礎(chǔ)結(jié)構(gòu)權(quán)限。
        IMessageSink NextSink { get; }
        [SecurityCritical]
        IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink);
        [SecurityCritical]
        IMessage SyncProcessMessage(IMessage msg);
    }

在代碼的定義可以看到喲給NextSink的屬性,用以鏈接在管道中的下個消息接收器并且已這樣的形式形成消息接收器鏈(單向鏈表?職責(zé)鏈模式?),對于AsyncProcessMessage()方法暫且不去聊它,SyncProcessMessage()方法就可以理解為是調(diào)用的遠程對象所執(zhí)行的函數(shù)。來看下,示例代碼1-7.

代碼1-7

  public class AOPWriterSink : IMessageSink
    {
        private IMessageSink m_NextSink;
        public AOPWriterSink(IMessageSink nextSink)
        {
            m_NextSink = nextSink;
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }

        public IMessageSink NextSink
        {
            get { return m_NextSink; }
        }

        public IMessage SyncProcessMessage(IMessage msg)
        {


            IMethodCallMessage callMessage = msg as IMethodCallMessage;
            if (callMessage.MethodName == "WriterLine")
            {
                Context context = Thread.CurrentContext;
                AOPContextProperty contextWriterService =
                    context.GetProperty("ContextService") as AOPContextProperty;
                if (callMessage == null)
                {
                    return null;
                }
                IMessage retMsg = null;
                if (contextWriterService != null)
                {
                    contextWriterService.WriterMessage("方法調(diào)用之前");
                }
                retMsg = m_NextSink.SyncProcessMessage(msg);
                contextWriterService.WriterMessage("方法調(diào)用之后");
                return retMsg;
            }
            else
            {
                return m_NextSink.SyncProcessMessage(msg);
            }
        }
    }

在實例化綁定上下文對象的時候或者是調(diào)用定上下文對象的方法的時候都會調(diào)用SyncProcessMessage()方法,在SyncProcessMessage()方法中我們根據(jù)IMessage消息對象來獲取當前遠程對象執(zhí)行方法的名稱(對應(yīng)代碼1-1中的對象的函數(shù)名稱),隨之獲取當前上下文屬性,利用上下文屬性中的邏輯操作來進行攔截后的操作。來看下測試代碼結(jié)果如圖5,示例代碼1-8.

代碼1-8

        static void Main(string[] args)
        {
            MyContextObject myContextObject = new MyContextObject();
            myContextObject.WriterLine("Test");
            Console.ReadLine();
        }

圖5

.NET里簡易實現(xiàn)AOP

 

2消息接收器的執(zhí)行過程

這個小節(jié)我們來說明一下消息接收器的執(zhí)行過程,示例還是接著上個小節(jié)的內(nèi)容,需要再次定義套上面示例中的內(nèi)容,代碼近乎相同為了更清楚的說明所以示例出來。來看代碼定義,示例代碼2-1.

代碼2-1

 [AttributeUsage(AttributeTargets.Class)]
    public class AOPTestAttribute : Attribute, IContextAttribute
    {
        public void GetPropertiesForNewContext(IConstructionCallMessage msg)
        {
            msg.ContextProperties.Add(new AOPContextPropertyTest());
           
        }
        public bool IsContextOK(System.Runtime.Remoting.Contexts.Context ctx, IConstructionCallMessage msg)
        {
            return false;
        }
    }

首先還是上下文綁定對象的標識特性定義,用處上節(jié)中說過了,再來看對應(yīng)的上下文屬性定義,示例代碼2-2.

代碼2-2

     public class AOPContextPropertyTest : IContextProperty, IContributeServerContextSink
    {

        public void Freeze(Context newContext)
        {

        }

        public bool IsNewContextOK(Context newCtx)
        {

            return true;
        }

        public string Name
        {
            get { return "ContextServiceTest"; }
        }

        /// <summary>
        /// 提供的服務(wù)
        /// </summary>
        /// <param name="meg"></param>
        public void WriterMessage(string meg)
        {
            Console.WriteLine(meg);
        }

        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            AOPWriterSinkTest megSink = new AOPWriterSinkTest(nextSink);
            return megSink;
        }
    }

這里上下文屬性的定義不同于上面的內(nèi)容是對于屬性Name值的修改以及在設(shè)置消息接收器鏈的實現(xiàn)中重新設(shè)置了新定義的消息接收器,我們看一下新消息接收器的定義,示例代碼2-3

代碼2-3

  public class AOPWriterSinkTest : IMessageSink
    {
        private IMessageSink m_NextSink;
        public AOPWriterSinkTest(IMessageSink nextSink)
        {
            m_NextSink = nextSink;
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }

        public IMessageSink NextSink
        {
            get { return m_NextSink; }
        }

        public IMessage SyncProcessMessage(IMessage msg)
        {


            IMethodCallMessage callMessage = msg as IMethodCallMessage;
            if (callMessage.MethodName == "WriterLine")
            {
                Context context = Thread.CurrentContext;
                AOPContextPropertyTest contextWriterService =
                    context.GetProperty("ContextServiceTest") as AOPContextPropertyTest;
                if (callMessage == null)
                {
                    return null;
                }
                IMessage retMsg = null;
                if (contextWriterService != null)
                {
                    contextWriterService.WriterMessage("方法調(diào)用之前TEST");
                }
                retMsg = m_NextSink.SyncProcessMessage(msg);
                contextWriterService.WriterMessage("方法調(diào)用之后TEST");
                return retMsg;
            }
            else
            {
                return m_NextSink.SyncProcessMessage(msg);
            }
        }
    }

在代碼2-3中SyncProcessMessage()方法中的實現(xiàn)也有所改變,對于獲取上下文屬性的參數(shù)修改為2-2中定義的屬性名稱了。修改代碼1-1的內(nèi)容,修改為代碼2-4.

代碼2-4

    [AOPWriter]
    [AOPTest]
    public class MyContextObject : ContextBoundObject
    {
        public void WriterLine(string meg)
        {
            Console.WriteLine("這是方法執(zhí)行中" + meg);
        }
    }

最后我們看一下結(jié)果圖6.

圖6

.NET里簡易實現(xiàn)AOP

有的朋友可能疑問了,為什么AOPWriter在AOPTest之前而執(zhí)行的結(jié)果明顯的是AOPTest部分的內(nèi)容先執(zhí)行了,這確實跟執(zhí)行關(guān)系有很大的關(guān)系,在系統(tǒng)首先執(zhí)行的時候會先設(shè)置AOPWriter部分所對應(yīng)的消息接收器到新建上下文中消息接收器鏈的前端,隨后在設(shè)置AOPTest部分的消息接收器的時候又是重復(fù)的執(zhí)行上述的操作了,所以AOPTest部分的接收器排在了前面,所以先執(zhí)行了??词疽鈭D7所表示的。

圖7

.NET里簡易實現(xiàn)AOP

看到這里想必大家已經(jīng)清楚的知道了消息接收器的設(shè)置過程了,但是對消息接收器鏈的執(zhí)行過程并不清楚,我們再橫向的看一下消息接收器鏈執(zhí)行的時候是個什么樣的過程,如圖8.

圖8

.NET里簡易實現(xiàn)AOP

在起初的AOPWriterSinkTest消息接收器中執(zhí)行完攔截的Before操作時,會調(diào)用AOPWriterSinkTest消息接收器中的SyncProcessMessage()將消息對象往下面的消息器接收器中傳遞,到最后AOPWriterSink也執(zhí)行完Before操作時再次向下傳遞的時候沒有發(fā)現(xiàn)消息接收器了,便會調(diào)用遠程對象所需執(zhí)行的方法,在方法執(zhí)行完畢后執(zhí)行AOPWriterSink中的After操作,在AOPWriterSink的After操作執(zhí)行完畢后消息也會隨著起初AOPWriterSinkTest消息接收器中的SyncProcessMessage()返回,返回到了AOPWriterSinkTest消息接收器中,之后再執(zhí)行它的After操作,這時的結(jié)果就如同圖6中所演示的那樣。

 

3 AOP的概念轉(zhuǎn)換

在上面的小節(jié)中是講解說明了利用remoring的技術(shù)來進行消息攔截,以及所用的詞匯都是remoting中的類型詞匯,在本節(jié)將會對上面的內(nèi)容進行抽象,并且已AOP的概念來描述這些類型。

首先我們會對上節(jié)中實現(xiàn)了IContextAttribute類型的標識上下文對象進行抽象,其實也就是對AOP中關(guān)注點(切面)的抽象,來看代碼定義3-1.

代碼3-1

using System.Runtime.Remoting;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Activation;

namespace FrameWork.AOP.Achieve.AOPBasics
{
    [AttributeUsage(AttributeTargets.Class)]
    public abstract class AOPAttribute : Attribute, IContextAttribute
    {
        public AOPAttribute() { }
        public abstract AOPProperty CreateContextProperty();

        public void GetPropertiesForNewContext(IConstructionCallMessage msg)
        {
            AOPProperty contextProperty = CreateContextProperty();
            msg.ContextProperties.Add(contextProperty);
        }

        public bool IsContextOK(Context ctx, IConstructionCallMessage msg)
        {
            return false;
        }
    }
}

代碼3-1中定義和上面近似相同,只不過把需要添加的到切面中的屬性(上下文屬性)的實現(xiàn)是通過CreateContextProperty()方法來實現(xiàn)的,并且CreateContextProperty()方法定義為抽象的由實現(xiàn)類來提供具體的實現(xiàn),這樣做的好處可以讓實現(xiàn)更加的靈活添置更多的可擴展點。

在對關(guān)注點(切面)進行抽象后下面要對切面中的屬性進行抽象,這里我感覺它的作用在于兩點,第一點是功能上的是用于連接抽象關(guān)注點和具體關(guān)注點實現(xiàn)的類別,第二點就是用于細化關(guān)注點,也就是第一點中說到的一個關(guān)注點中可能有N種的類別,可能這里這樣說大家有點不理解什么點不點面不面的亂七八糟的概念,我這里舉個例子,比如說在上面小節(jié)中我們對一個完成的執(zhí)行過程進行了橫切,把日志、性能監(jiān)測等關(guān)注點分離出原執(zhí)行過程,然而在日志這個關(guān)注點中,可能會存在普通日志、性能日志、錯誤日志等,對于關(guān)注點的抽象和關(guān)注點的抽象可以很好的解決這一點,也就是對關(guān)注點再次的抽象,下面我們看一下關(guān)注點屬性的代碼定義,示例代碼3-2

using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Messaging;

namespace FrameWork.AOP.Achieve.AOPBasics
{
    public abstract class AOPProperty : IContextProperty, IContributeServerContextSink
    {
        public AOPProperty() { }
        public void Freeze(Context newContext)
        {
            
        }

        public bool IsNewContextOK(Context newCtx)
        {
            return true;
        }

        public string Name
        {
            get { return GetName(); }
        }

        protected virtual string GetName()
        {
            return "AOP";
        }

        protected abstract IMessageSink CreateAOPAspect(IMessageSink nextSink);

        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            AOPAspect aopAspect = CreateAOPAspect(nextSink) as AOPAspect;
            aopAspect.AOPPropertyName = Name;
            return (IMessageSink)aopAspect;
        }
    }
}

同樣的在代碼3-2中,我們將具體關(guān)注點執(zhí)行類型(并不是最終執(zhí)行的地方)的生成同樣是放到了實現(xiàn)具體關(guān)注點中的實現(xiàn)了,并且把抽象具體關(guān)注點的Name屬性值設(shè)置為GetName(),GetName()方法的定義也是可以由實現(xiàn)具體關(guān)注點來定義。下面我們再來看一下具體關(guān)注點執(zhí)行類型的定義,示例代碼3-3

代碼3-3

using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using FrameWork.AOP.Achieve.Config;

namespace FrameWork.AOP.Achieve.AOPBasics
{
    public abstract class AOPAspect : IMessageSink
    {
        private IMessageSink _NextSink;

        private string _AOPPropertyName;

        public string AOPPropertyName
        {
            get { return _AOPPropertyName; }
            set { _AOPPropertyName = value; }
        }

        public AOPAspect(IMessageSink nextsink)
        {
            _NextSink = nextsink;
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }

        public IMessageSink NextSink
        {
            get { return _NextSink; }
        }

        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMethodCallMessage callmsg = msg as IMethodCallMessage;
            if (callmsg == null)
            {
                return null;
            }
            IMessage resultMsg = null;
            BeforeAchieve();
            resultMsg = _NextSink.SyncProcessMessage(msg);
            AfterAchieve();
            return resultMsg;
        }

        protected virtual void BeforeAchieve()
        {

        }

        protected virtual void AfterAchieve ()
        {

        }
    }
}

我們在代碼3-3中可以看到具體關(guān)注點執(zhí)行類型的定義,并且把真正的執(zhí)行包含在了具體關(guān)注點執(zhí)行類型中(真正的執(zhí)行是指攔截后的具體操作BeforeAchieve和AfterAchieve方法),這個是不妥的,我們會在下文中說到,并且會把它分離出去。

現(xiàn)在我們來看一下上面所說概念的對應(yīng)關(guān)系,如圖9

圖9

.NET里簡易實現(xiàn)AOP

對于關(guān)注點和具體關(guān)注點的對應(yīng)關(guān)系是1對N的關(guān)系,而具體關(guān)注點和具體關(guān)注點執(zhí)行類型的關(guān)系是N對N的。

現(xiàn)在我們來測試一下上面所示例的代碼內(nèi)容,會發(fā)現(xiàn)暴露的問題。

首先我們實現(xiàn)關(guān)注點,示例代碼3-4.

代碼3-4

using FrameWork.AOP.Achieve.AOPBasics;

namespace FrameWork.AOP.Test.Log
{
    public class AOPLog:AOPAttribute
    {
        public override AOPProperty CreateContextProperty()
        {
            return new AOPLogProperty();
        }
    }
}

之后我們再實現(xiàn)一個具體關(guān)注點,示例代碼3-5

代碼3-5

using FrameWork.AOP.Achieve.AOPBasics;
using System.Runtime.Remoting.Messaging;

namespace FrameWork.AOP.Test.Log
{
    public class AOPLogProperty : AOPProperty
    {
        protected override IMessageSink CreateAOPAspect(IMessageSink nextSink)
        {
            return new AOPLogAspect(nextSink);
        }

        protected override string GetName()
        {
            return "AOPLog";
        }
    }
}

隨之再實現(xiàn)一個具體關(guān)注點執(zhí)行類型,示例代碼3-6

代碼3-6

using FrameWork.AOP.Achieve.AOPBasics;
using System.Runtime.Remoting.Messaging;

namespace FrameWork.AOP.Test.Log
{
    public class AOPLogAspect:AOPAspect
    {
        public AOPLogAspect(IMessageSink nextSink)
            : base(nextSink)
        { }

        protected override void BeforeAchieve()
        {
            Console.WriteLine("MethodBefore");
        }
        protected override void AfterAchieve()
        {
            Console.WriteLine("MethodAfter");
        }
    }
}

我們再看一下測試用例,示例代碼3-7

     [AOPLog]
    public class TestInstance : ContextBoundObject
    {
        public void TestMethod()
        {
            Console.WriteLine("MethodAction……");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            TestInstance testInstance = new TestInstance();
            testInstance.TestMethod();
            Console.ReadLine();
        }
    }

最后我們看下測試結(jié)果,如圖10.

圖10

.NET里簡易實現(xiàn)AOP

從這里我們可以發(fā)現(xiàn)一個問題,就是對于信息的攔截上面的定義是不能具體區(qū)分的,以至于在遠程對象實例化的時候,連構(gòu)造函數(shù)都執(zhí)行了一遍攔截,這是我們不希望看到的,我們希望可以對此進行控制,第二個問題就是具體用于執(zhí)行攔截的操作是不應(yīng)該包含在具體關(guān)注點執(zhí)行類型中的。

針對這兩個問題我們要做出修改,第一通過配置信息來對信息攔截做控制,希望攔截我們所想要攔截的信息,第二將具體的執(zhí)行操作從具體關(guān)注點執(zhí)行類型中分離出來,通過配置信息來操作。

在做具體修改之前我們再看一下上面所有概念的關(guān)系,如圖11

圖11

.NET里簡易實現(xiàn)AOP

這里的配置信息所要包含信息要有如下幾點:

第一 需要有我們要攔截信息的標識

第二 所對應(yīng)的自定義執(zhí)行的類型

第三 攔截信息的類型

現(xiàn)在我們再來看一下圖11經(jīng)過對象化抽象過后的示意圖,圖12

圖12

.NET里簡易實現(xiàn)AOP

我們先從執(zhí)行過程的最末端開始實現(xiàn),也就是攔截后所要做的具體操作,來看示例代碼3-8

代碼3-8

using System.Runtime.Remoting.Messaging;

namespace FrameWork.AOP.Achieve.AOPCustomAchieve
{
    public interface IBeforeAchieve
    {
        void BeforeAchieve(IMethodCallMessage callMsg);
    }
    public interface IAfterAchieve
    {
        void AfterAchieve(IMethodReturnMessage returnMsg);
    }
    public abstract class AOPInterceptAchieve:IBeforeAchieve,IAfterAchieve
    {
        public abstract void BeforeAchieve(IMethodCallMessage callMsg);

        public abstract void AfterAchieve(IMethodReturnMessage returnMsg);
    }

}

對于代碼3-8不用說什么了,很明了。下面我們要對配置文件進行實現(xiàn),示例代碼3-9

代碼3-9

using FrameWork.AOP.Achieve.AOPCustomAchieve;

namespace FrameWork.AOP.Achieve.Config
{
    public enum InterceptAchieveType
    {
        Before,
        After,
        All
    }
    public class Config
    {
        private List<string> _MethodNameList;

        public List<string> MethodNameList
        {
            get { return _MethodNameList; }
        }

        private InterceptAchieveType _InterceptActhieveType;

        public InterceptAchieveType InterceptActhieveType
        {
            get { return _InterceptActhieveType; }
            set { _InterceptActhieveType = value; }
        }

        private AOPInterceptAchieve _AOPInterceptAchieve;

        public AOPInterceptAchieve AOPInterceptAchieve
        {
            get { return _AOPInterceptAchieve; }
            set { _AOPInterceptAchieve = value; }
        }

        public Config()
        {
            _MethodNameList = new List<string>();
            _InterceptActhieveType = InterceptAchieveType.All;
        }
        public Config(List<string> methodNameList, InterceptAchieveType interceptActhieveType)
        {
            _MethodNameList = methodNameList;
            _InterceptActhieveType = interceptActhieveType;
        }

        public void AddMethodName(string methodName)
        {
            if (string.IsNullOrEmpty(methodName))
            {
                throw new ArgumentNullException("methodName");
            }
            _MethodNameList.Add(methodName);
        }

        public void SetInterceptActhieveType(InterceptAchieveType interceptAchieveType)
        {
            _InterceptActhieveType = interceptAchieveType;
        }

        public void SetAOPInterceptAchieve(AOPInterceptAchieve aopInterceptAchieve)
        {
            if (aopInterceptAchieve == null)
            {
                throw new ArgumentNullException("aopInterceptAchieve");
            }
            _AOPInterceptAchieve = aopInterceptAchieve;
        }
    }
}

這里的配置類可以用配置文件來表示,可以表示為一個節(jié)點中所包含的信息,不過在這里我是出于簡便的考慮,省去了從配置文件讀取信息后轉(zhuǎn)化為對象的這么一個過程。在代碼3-9中定義了一個枚舉類型用于設(shè)置攔截的操作類型,然后就是這個配置所要對應(yīng)的攔截方法的名稱集合以及具體執(zhí)行操作的這么一個自定義操作抽象基類。

最后我們看一下處理配置信息類的配置信息處理引擎的定義,示例代碼3-10。

代碼3-10

using FrameWork.AOP.Achieve.AOPCustomAchieve;
using System.Runtime.Remoting.Messaging;

namespace FrameWork.AOP.Achieve.Config
{
    public class ConfigRelationExecution
    {
        private static Dictionary<string, Config> _ConfigRelation;

        public static Dictionary<string, Config> ConfigRelation
        {
            get 
            {
                if (_ConfigRelation == null)
                {
                    _ConfigRelation = new Dictionary<string, Config>();
                }
                return _ConfigRelation;
            }
        }

        public static void ActionConfigByAspectName(string aspectname, string methodname, InterceptAchieveType interceptAchieveType, IMessage msg)
        {
            if (_ConfigRelation.ContainsKey(aspectname))
            {
                Config config = _ConfigRelation[aspectname];
                if (config.MethodNameList.Contains(methodname))
                {
                    if (config.AOPInterceptAchieve != null)
                    {
                        if (config.InterceptActhieveType == InterceptAchieveType.All)
                        {
                            InterceptAchieve(interceptAchieveType, config.AOPInterceptAchieve, msg);
                        }
                        else if (config.InterceptActhieveType == interceptAchieveType)
                        {
                            InterceptAchieve(interceptAchieveType, config.AOPInterceptAchieve, msg);
                        }
                    }
                }
            }
        }

        private static void InterceptAchieve(InterceptAchieveType interceptAchieveType, AOPInterceptAchieve aopInterceptAchieve,IMessage msg)
        {
            if (interceptAchieveType == InterceptAchieveType.Before)
            {
                IBeforeAchieve beforeAchieve = aopInterceptAchieve as IBeforeAchieve;
                if (beforeAchieve != null)
                {
                    beforeAchieve.BeforeAchieve((IMethodCallMessage)msg);
                }
            }
            else
            {
                IAfterAchieve beforeAchieve = aopInterceptAchieve as IAfterAchieve;
                if (beforeAchieve != null)
                {
                    beforeAchieve.AfterAchieve((IMethodReturnMessage)msg);
                }
            }
        }
    }
}

在代碼3-10配置信息處理類中,我設(shè)置了ConfigRelation屬性,這個屬性的作用是用來保存具體關(guān)注點執(zhí)行類型和配置信息的對應(yīng)關(guān)系的,對于ActionConfigByAspectName()方法和InterceptAchieve()方法也就是一個邏輯判斷的操作。根據(jù)當前具體關(guān)注點的名稱來做一些判斷。

現(xiàn)在我們修改一下代碼3-3中的內(nèi)容,修改為代碼3-11

代碼3-11

using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using FrameWork.AOP.Achieve.Config;

namespace FrameWork.AOP.Achieve.AOPBasics
{
    public abstract class AOPAspect : IMessageSink
    {
        private IMessageSink _NextSink;

        public AOPAspect(IMessageSink nextsink)
        {
            _NextSink = nextsink;
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }

        public IMessageSink NextSink
        {
            get { return _NextSink; }
        }
        private string _AOPPropertyName;

        public string AOPPropertyName
        {
            get { return _AOPPropertyName; }
            set { _AOPPropertyName = value; }
        }
        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMethodCallMessage callmsg = msg as IMethodCallMessage;
            if (callmsg == null)
            {
                return null;
            }
            IMessage resultMsg = null;
            ConfigRelationExecution.ActionConfigByAspectName(_AOPPropertyName, callmsg.MethodName, InterceptAchieveType.Before, callmsg);
            resultMsg = _NextSink.SyncProcessMessage(msg);
            ConfigRelationExecution.ActionConfigByAspectName(_AOPPropertyName, callmsg.MethodName, InterceptAchieveType.After, resultMsg);
            return resultMsg;
        }
    }
}

在這之后再修改下代碼3-6的內(nèi)容,刪除掉兩個重寫基類的方法。

最后我們還是要實現(xiàn)一個定義攔截的操作,來看示例代碼3-12

代碼3-12

using System.Runtime.Remoting.Messaging;
using FrameWork.AOP.Achieve;
using FrameWork.AOP.Achieve.AOPCustomAchieve;

namespace FrameWork.AOP.Test.Log.Achieve
{
    public class LogWriterAchieve:AOPInterceptAchieve
    {
        public override void BeforeAchieve(IMethodCallMessage callMsg)
        {
            if (callMsg == null)
            {
                return;
            }
            Console.WriteLine(callMsg.MethodName + "—Before—" + this.GetType().Name);
        }

        public override void AfterAchieve(IMethodReturnMessage returnMsg)
        {
            if (returnMsg == null)
            {
                return;
            }
            Console.WriteLine(returnMsg.MethodName + "—After—" + this.GetType().Name);
        }
    }
}

最后我們來看下測試代碼,示例3-13

代碼3-13

class Program
    {
        static void Main(string[] args)
        {
            Config config = new Config();
            config.AddMethodName("TestMethod");
            config.InterceptActhieveType = InterceptAchieveType.Before;
            config.AOPInterceptAchieve = new Log.Achieve.LogWriterAchieve();
            ConfigRelationExecution.ConfigRelation.Add("AOPLog", config);
            TestInstance testInstance = new TestInstance();
            testInstance.TestMethod();
            Console.ReadLine();
        }
    }

這里可以修改config.InterceptActhieveType的值依次是Before、After、All,顯示的結(jié)果如下圖。

.NET里簡易實現(xiàn)AOP

.NET里簡易實現(xiàn)AOP

.NET里簡易實現(xiàn)AOP

還有很多測試用例就不一一的列舉了,不過這里還要提一句的就是利用remoting來實現(xiàn)的AOP是不能滿足正常運用的有個弊端,因為在遠程對象方法內(nèi)部調(diào)用的是遠程對象內(nèi)部的另一個方法時,攔截到的只能是調(diào)用的方法,被調(diào)用的則不行。

到這里對于AOP的講解實現(xiàn)已經(jīng)說完了,謝謝大家的閱讀。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI