<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 71  文章 - 15  trackbacks - 0
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    因?yàn)榭诳剩系蹌?chuàng)造了水;
    因?yàn)楹诎担系蹌?chuàng)造了火;
    因?yàn)槲倚枰笥眩陨系圩屇銇淼轿疑磉?br> Click for Shaanxi xi'an, Shaanxi Forecast
    ╱◥█◣
      |田|田|
    ╬╬╬╬╬╬╬╬╬╬╬
    If only I have such a house!
    〖總在爬山 所以艱辛〗
    Email:myesjoy@yahoo.com.cn
    NickName:yesjoy
    MSN:myesjoy@hotmail.com
    QQ:150230516

    〖總在尋夢(mèng) 所以苦痛〗

    常用鏈接

    留言簿(3)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    Hibernate在線

    Java友情

    Java認(rèn)證

    linux經(jīng)典

    OA系統(tǒng)

    Spring在線

    Structs在線

    專家專欄

    企業(yè)信息化

    大型設(shè)備共享系統(tǒng)

    工作流

    工作流產(chǎn)品

    網(wǎng)上購書

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    4.1.Net平臺(tái)AOP技術(shù)概覽

    .Net平臺(tái)與Java平臺(tái)相比,由于它至今在服務(wù)端仍不具備與unix系統(tǒng)的兼容性,也不具備類似于Java平臺(tái)下J2EE這樣的企業(yè)級(jí)容器,使得.Net平臺(tái)在大型的企業(yè)級(jí)應(yīng)用上,常常為人所詬病。就目前而言,.Net平臺(tái)并沒有提供AOP技術(shù)的直接實(shí)現(xiàn),而微軟在未來對(duì)于.Net的發(fā)展戰(zhàn)略目標(biāo),我們?nèi)晕纯芍5蚁嘈盼④泴?duì)于目前炙手可熱的AOP技術(shù)應(yīng)該不會(huì)視而不見。也許在未來的.Net平臺(tái)下,會(huì)出現(xiàn)類似于Spring那樣的輕量級(jí)IoC容器,加上O/R Mapping的進(jìn)一步實(shí)現(xiàn)與完善,隨著Windows Server操作系統(tǒng)的逐步推新,.Net平臺(tái)對(duì)于企業(yè)級(jí)系統(tǒng)開發(fā)的支持會(huì)越來越多。

    AOP技術(shù)在.Net平臺(tái)中的應(yīng)用,相較于Java平臺(tái)而言,還遠(yuǎn)不夠成熟,功能也相對(duì)較弱,目前能夠投入商用的AOP工具幾乎沒有。借鑒Java開源社區(qū)的成功,.Net平臺(tái)下AOP工具的開發(fā)也都依托于開源社區(qū)的力量。眾多開源愛好者,仍然在堅(jiān)持不懈對(duì)AOP技術(shù)進(jìn)行研究和實(shí)踐,試圖找到AOP技術(shù)與.Net之間的完美結(jié)合點(diǎn),從而開發(fā)出真正能夠商用的功能強(qiáng)大的AOP工具。就目前而言,大部分在.Net平臺(tái)下的AOP工具,大部分均脫胎于Java平臺(tái)下的AOP工具,例如Spring.Net之于Spring,Eos之于AspectJ。由于Java平臺(tái)和.Net平臺(tái)在語言機(jī)制上的相似性,使得它們?cè)趯?shí)現(xiàn)AOP的技術(shù)機(jī)制上,大體相似,無非是利用靜態(tài)織入或動(dòng)態(tài)織入的方式,完成對(duì)aspect的實(shí)現(xiàn)。

    目前在.Net平臺(tái)下的AOP大部分仍然處于最初的開發(fā)階段,各自發(fā)布的版本基本都是beta版。其中較有代表性的AOP工具包括Aspect#,Spring.Net,Eos等。

    Aspect#是基于Castle動(dòng)態(tài)代理技術(shù)實(shí)現(xiàn)的。Castle動(dòng)態(tài)代理技術(shù)利用了.Net的Emit技術(shù),生成一個(gè)新的類去實(shí)現(xiàn)特定的接口,或者擴(kuò)展一個(gè)已有的類,并將其委托指向IInterceptor接口的實(shí)現(xiàn)類。通過Castle動(dòng)態(tài)代理技術(shù),就可以攔截方法的調(diào)用,并將Aspect的業(yè)務(wù)邏輯織入到方法中。利用Castle動(dòng)態(tài)代理技術(shù),最大的缺陷是它只對(duì)虛方法有效,這限制了Aspect#的一部分應(yīng)用。

    Spring.Net從根本意義上來說,是對(duì)Spring工具從Java平臺(tái)向.Net平臺(tái)的完全移植。它在AOP的實(shí)現(xiàn)上與Spring幾乎完全相似,仍然利用了AOP聯(lián)盟提供的攔截器、Advice等實(shí)現(xiàn)AOP。Spring.Net的配置文件也與Spring相同。

    Eos采用的是靜態(tài)織入的技術(shù)。它提供了獨(dú)有的編譯器,同時(shí)還擴(kuò)展了C#語法,以類似于AspectJ的結(jié)構(gòu),規(guī)定了一套完整的AOP語法,諸如aspect,advice,before,after,pointcut等。Eos充分的利用了.Net中元數(shù)據(jù)的特點(diǎn),以IL級(jí)的代碼對(duì)方面進(jìn)行織入,這也使得它的性能與其他AOP工具比較有較大的提高。

    4.2 .Net平臺(tái)下實(shí)現(xiàn)AOP的技術(shù)基礎(chǔ)

    如前所述,在.Net平臺(tái)下實(shí)現(xiàn)AOP,采用的方式主要是靜態(tài)織入和動(dòng)態(tài)織入的方式。在本文中,我將充分利用.Net的技術(shù)特性,包括元數(shù)據(jù)、Attribute、.Net Remoting的代理技術(shù),將其綜合運(yùn)用,最終以動(dòng)態(tài)織入的方式實(shí)現(xiàn)AOP公共類庫。本節(jié)將介紹實(shí)現(xiàn)AOP所必需的.Net知識(shí)。

    4.2.1元數(shù)據(jù)(metadata)
    4.2.1.1元數(shù)據(jù)概述

    元數(shù)據(jù)是一種二進(jìn)制信息,用以對(duì)存儲(chǔ)在公共語言運(yùn)行庫(CLR)中可移植可執(zhí)行文件 (PE) 或存儲(chǔ)在內(nèi)存中的程序進(jìn)行描述。在.Net中,如果將代碼編譯為 PE 文件時(shí),便會(huì)將元數(shù)據(jù)插入到該文件的一部分中,而該代碼被編譯成的Microsoft 中間語言 (MSIL),則被插入到該文件的另一部分中。在模塊或程序集中定義和引用的每個(gè)類型和成員都將在元數(shù)據(jù)中進(jìn)行說明。執(zhí)行代碼時(shí),運(yùn)行庫將元數(shù)據(jù)加載到內(nèi)存中,并引用它來發(fā)現(xiàn)有關(guān)代碼的類、成員、繼承等信息。

    元數(shù)據(jù)以非特定語言的方式描述在代碼中定義的每一類型和成員。它存儲(chǔ)的信息包括程序集的信息,如程序集的版本、名稱、區(qū)域性和公鑰,以及該程序集所依賴的其他程序集;此外,它還包括類型的說明,包括類型的基類和實(shí)現(xiàn)的接口,類型成員(方法、字段、屬性、事件、嵌套的類型)。

    在.Net Framework中,元數(shù)據(jù)是關(guān)鍵,該模型不再需要接口定義語言 (IDL) 文件、頭文件或任何外部組件引用方法。元數(shù)據(jù)允許 .NET 語言自動(dòng)以非特定語言的方式對(duì)其自身進(jìn)行描述,此外,通過使用Attribute,可以對(duì)元數(shù)據(jù)進(jìn)行擴(kuò)展。元數(shù)據(jù)具有以下主要優(yōu)點(diǎn):

    1. 自描述文件

    公共語言運(yùn)行庫(CLR)模塊和程序集是自描述的。模塊的元數(shù)據(jù)包含與另一個(gè)模塊進(jìn)行交互所需的全部信息。元數(shù)據(jù)自動(dòng)提供COM中IDL的功能,允許將一個(gè)文件同時(shí)用于定義和實(shí)現(xiàn)。運(yùn)行庫模塊和程序集甚至不需要向操作系統(tǒng)注冊(cè)。運(yùn)行庫使用的說明始終反映編譯文件中的實(shí)際代碼,從而提高應(yīng)用程序的可靠性。

    2.語言互用性和更簡(jiǎn)單的基于組件的設(shè)計(jì)

    元數(shù)據(jù)提供所有必需的有關(guān)已編譯代碼的信息,以供您從用不同語言編寫的 PE 文件中繼承類。您可以創(chuàng)建用任何托管語言(任何面向公共語言運(yùn)行庫的語言)編寫的任何類的實(shí)例,而不用擔(dān)心顯式封送處理或使用自定義的互用代碼。

    3.Attribute

    .NET Framework允許在編譯文件中聲明特定種類的元數(shù)據(jù)(稱為Attribute)。在整個(gè) .NET Framework 中到處都可以發(fā)現(xiàn)Attribute的存在,Attribute用于更精確地控制運(yùn)行時(shí)程序如何工作。另外,用戶可以通過自定義屬性向 .NET Framework 文件發(fā)出用戶自己的自定義元數(shù)據(jù)。

    4.2.1.2元數(shù)據(jù)的結(jié)構(gòu)

    在PE文件中與元數(shù)據(jù)有關(guān)的主要包括兩部分。一部分是元數(shù)據(jù),它包含一系列的表和堆數(shù)據(jù)結(jié)構(gòu)。每個(gè)元數(shù)據(jù)表都保留有關(guān)程序元素的信息。例如,一個(gè)元數(shù)據(jù)表說明代碼中的類,另一個(gè)元數(shù)據(jù)表說明字段等。如果您的代碼中有10個(gè)類,類表將有10行,每行為1個(gè)類。元數(shù)據(jù)表引用其他的表和堆。例如,類的元數(shù)據(jù)表引用方法表。元數(shù)據(jù)以四種堆結(jié)構(gòu)存儲(chǔ)信息:字符串、Blob、用戶字符串和 GUID。所有用于對(duì)類型和成員進(jìn)行命名的字符串都存儲(chǔ)在字符串堆中。例如,方法表不直接存儲(chǔ)特定方法的名稱,而是指向存儲(chǔ)在字符串堆中的方法的名稱。

    另一部分是MSIL指令,許多MSIL指令都帶有元數(shù)據(jù)標(biāo)記。元數(shù)據(jù)標(biāo)記在 PE 文件的 MSIL 部分中唯一確定每個(gè)元數(shù)據(jù)表的每一行。元數(shù)據(jù)標(biāo)記在概念上和指針相似,永久駐留在MSIL中,引用特定的元數(shù)據(jù)表。元數(shù)據(jù)標(biāo)記是一個(gè)四個(gè)字節(jié)的數(shù)字。最高位字節(jié)表示特定標(biāo)記(方法、類型等)引用的元數(shù)據(jù)表。剩下的三個(gè)字節(jié)指定與所說明的編程元素對(duì)應(yīng)的元數(shù)據(jù)表中的行。如果用C#定義一個(gè)方法并將其編譯到PE文件中,下面的元數(shù)據(jù)標(biāo)記可能存在于PE文件的MSIL部分:
    0×06000004

    最高位字節(jié) (0×06) 表示這是一個(gè)MethodDef標(biāo)記。低位的三個(gè)字節(jié) (000004) 指示公共語言運(yùn)行庫在 MethodDef 表的第四行查找對(duì)該方法定義進(jìn)行描述的信息。

    表4.1 描述了PE文件中元數(shù)據(jù)的結(jié)構(gòu)及其每部分的內(nèi)容:

    PE部分     

     

     

     

    PE部分的內(nèi)容     

     

     

     

    表4.1  PE文件中的元數(shù)據(jù)

    4.2.1.3元數(shù)據(jù)在運(yùn)行時(shí)的作用
    由于在MSIL指令中包含了元數(shù)據(jù)標(biāo)記,因此,當(dāng)公共語言運(yùn)行庫(CLR)將代碼加載到內(nèi)存時(shí),將向元數(shù)據(jù)咨詢?cè)摯a模塊中包含的信息。運(yùn)行庫對(duì)Microsoft 中間語言 (MSIL) 流執(zhí)行廣泛的分析,將其轉(zhuǎn)換為快速本機(jī)指令。運(yùn)行庫根據(jù)需要使用實(shí)時(shí) (JIT) 編譯器將 MSIL 指令轉(zhuǎn)換為本機(jī)代碼,每次轉(zhuǎn)換一個(gè)方法。例如,有一個(gè)類APP,其中包含了Main()方法和Add()方法:
    using System; 

    public class App
    {
       public static int Main()
       {
          int ValueOne = 10;
          int ValueTwo = 20;       
          Console.WriteLine(”The Value is: {0}”, Add(ValueOne, ValueTwo));
          return 0;
       }
       public static int Add(int One, int Two)
       {
          return (One + Two);
       }
    }

    通過運(yùn)行庫,這段代碼被加載到內(nèi)存中,并被轉(zhuǎn)化為MSIL:
    .entrypoint
    .maxstack  3
    .locals ([0] int32 ValueOne,
             [1] int32 ValueTwo,
             [2] int32 V_2,
             [3] int32 V_3)
    IL_0000:  ldc.i4.s   10
    IL_0002:  stloc.0
    IL_0003:  ldc.i4.s   20
    IL_0005:  stloc.1
    IL_0006:  ldstr      “The Value is: {0}”
    IL_000b:  ldloc.0
    IL_000c:  ldloc.1
    IL_000d:  call int32 ConsoleApplication.MyApp::Add(int32,int32) /* 06000003 */

    JIT 編譯器讀取整個(gè)方法的 MSIL,對(duì)其進(jìn)行徹底地分析,然后為該方法生成有效的本機(jī)指令。在 IL_000d 遇到 Add 方法 (/* 06000003 */) 的元數(shù)據(jù)標(biāo)記,運(yùn)行庫使用該標(biāo)記參考 MethodDef 表的第三行。

    表4.2顯示了說明 Add 方法的元數(shù)據(jù)標(biāo)記所引用的 MethodDef 表的一部分:

        

     

     

     

    相對(duì)虛擬地址 (RVA)     

     

     

     

    ImplFlags     

     

     

     

    Flags     

     

     

     

    Name     

     

     

    (指向字符串堆)    

     

    Signature     

     

     

    (指向 Blob 堆)    

     

    表4.2 元數(shù)據(jù)標(biāo)記

    該表的每一列都包含有關(guān)代碼的重要信息。RVA 列允許運(yùn)行庫計(jì)算定義該方法的 MSIL 的起始內(nèi)存地址。ImplFlags 和 Flags 列包含說明該方法的位屏蔽(例如,該方法是公共的還是私有的)。Name 列對(duì)來自字符串堆的方法的名稱進(jìn)行了索引。Signature 列對(duì)在 Blob 堆中的方法簽名的定義進(jìn)行了索引。

    通過利用元數(shù)據(jù),我們就可以獲得類的相關(guān)信息。如上所述,在類APP的MethodDef表中,可以獲得類APP的三個(gè)方法,以及方法的Flags和方法簽名。而在.Net中,則提供了反射技術(shù),來支持這種對(duì)元數(shù)據(jù)信息的獲取。可以說,正是因?yàn)橛辛嗽獢?shù)據(jù),才使得AOP的攔截與織入功能的實(shí)現(xiàn)成為可能。

    4.2.2 Attribute
    4.2.2.1 Attribute概述

    通過對(duì).Net元數(shù)據(jù)的分析,我們知道可以通過Attribute來擴(kuò)展元數(shù)據(jù)。那么什么是Attribute?在MSDN中,Attribute被定義為“是被指定給某一聲明的一則附加的聲明性信息”。 我們可以通過Attribute來定義設(shè)計(jì)層面的信息以及運(yùn)行時(shí)(run-time)信息,也可以利用Attribute建立自描述(self-describing)組件。

    Attribute可應(yīng)用于任何目標(biāo)元素,我們可以通過AttributeTargets枚舉指定其施加的目標(biāo),AttributeTargets枚舉在.Net中的定義如下:
    public enum AttributeTargets
    {
       All=16383,
       Assembly=1,
       Module=2,
       Class=4,
       Struct=8,
       Enum=16,
       Constructor=32,
       Method=64,
       Property=128,
       Field=256,
       Event=512,
       Interface=1024,
       Parameter=2048,
       Delegate=4096,
       ReturnValue=8192
    }

    作為參數(shù)的AttributeTarges的值允許通過“或”操作來進(jìn)行多個(gè)值的組合,如果你沒有指定參數(shù),那么默認(rèn)參數(shù)就是All 。

    不管是.Net Framework提供的Attribute,還是用戶自定義Attribute,都是通過[]施加到目標(biāo)元素上。雖然Attribute的用法與通常的類型不一樣,但在.Net內(nèi)部,Attribute本質(zhì)上還是一個(gè)類。但是,Attribute類的實(shí)例化發(fā)生在編譯時(shí),而非運(yùn)行時(shí),因而達(dá)到了擴(kuò)展元數(shù)據(jù)的目的。一個(gè)Attribute的多個(gè)實(shí)例可應(yīng)用于同一個(gè)目標(biāo)元素;并且Attribute可由從目標(biāo)元素派生的元素繼承。

    4.2.2.2自定義Attribute
    .Net Framework支持用戶自定義Attribute。自定義Attribute的方法與定義類一樣,唯一不同之處是自定義的Attribute必須繼承Attribute類。Attribute類包含用于訪問和測(cè)試自定義Attribute的簡(jiǎn)便方法。其中,Attribute類的構(gòu)造函數(shù)為protected,只能被Attribute的派生類調(diào)用。Attribute類包含的方法主要為:

    1.三個(gè)靜態(tài)方法
    static Attribute GetCustomAttribute():這個(gè)方法有8種重載的版本,它被用來取出施加在類成員上指定類型的Attribute。
    static Attribute[] GetCustomAttributes(): 這個(gè)方法有16種重載版本,用來取出施加在類成員上指定類型的Attribute數(shù)組。
    static bool IsDefined():有八種重載版本,看是否指定類型的定制attribute被施加到類的成員上面。

    2.兩個(gè)實(shí)例方法
    bool IsDefaultAttribute(): 如果Attribute的值是默認(rèn)的值,那么返回true。
    bool Match():表明這個(gè)Attribute實(shí)例是否等于一個(gè)指定的對(duì)象。

    3.公共屬性
    TypeId: 得到一個(gè)唯一的標(biāo)識(shí),這個(gè)標(biāo)識(shí)被用來區(qū)分同一個(gè)Attribute的不同實(shí)例。

    通過自定義Attribute,可使得用戶自定義的信息與Attribute施加的類本身相關(guān)聯(lián)。例如,給定一個(gè)自定義的 .NET 屬性,我們就可以輕松地將調(diào)用跟蹤Attribute與類的方法相關(guān)聯(lián):
    public class Bar
    {
        [CallTracingAttribute(”In Bar ctor”)]
        public Bar() {}
        [CallTracingAttribute(”In Bar.Calculate method”)]
        public int Calculate(int x, int y){ return x + y; }
    }

    請(qǐng)注意,方括號(hào)中包含 CallTracingAttribute 和訪問方法時(shí)輸出的字符串。這是將自定義元數(shù)據(jù)與 Bar 的兩個(gè)方法相關(guān)聯(lián)的Attribute語法。該自定義的Attribute實(shí)現(xiàn),如下所示:
    using System;
    using System.Reflection;

    [AttributeUsage( AttributeTargets.ClassMembers, AllowMultiple = false )]
    public class CallTracingAttribute : Attribute
    {    
        private string m_TracingInfo;
        public CallTracingAttribute(string info)
        {
            m_TracingInfo = info;
        }
        public string TracingInfo
        {
            get {return tracingInfo;}
        }
    }

    通過自定義的CallTracingAttribute,將一段Tracing信息施加到類Bar的構(gòu)造函數(shù)和方法Calculate上。我們可以利用反射技術(shù)與Attribute類提供的方法,來獲得Bar類的元數(shù)據(jù)中包含的Attribute信息,如:
    public class Test
    {
        public static void Main(string[] args)
        {
            System.Reflection.MemberInfo info = typeof(Bar);
            CallTracingAttribute attribute = (CallTracingAttribute) Attribute.GetCustomAttribute(info,typeof(CallTracingAttribute));
            if (attribute != null)
            {
                 Console.WriteLine(“Tracing Information:{0}”,attribute.TracingInfo);
            }
        }
    }

    4.2.2.3上下文(Context)和Attribute
    所謂上下文(Context),是指一個(gè)邏輯上的執(zhí)行環(huán)境。每一個(gè)應(yīng)用程序域都有一個(gè)或多個(gè)Context,.Net中的所有對(duì)象都會(huì)在相應(yīng)的Context中創(chuàng)建和運(yùn)行。如圖4.1所示,它顯示了一個(gè)安全地存在于Context的對(duì)象:

    aop4.1.gif
    圖4.1 安全地存在于Context的對(duì)象

    在圖4.1中,上下文(Context)提供了錯(cuò)誤傳播、事務(wù)管理和同步功能,而對(duì)象的創(chuàng)建和運(yùn)行就存在于該Context中。在.Net中,提供了ContextBoundObject類,它代表的含義就是該對(duì)象應(yīng)存在于指定的Context邊界中(Object that will be bound with a context)。凡是繼承了ContextBoundObject類的類類型,就自動(dòng)具備了對(duì)象與Context之間的關(guān)系。事實(shí)上,如果一個(gè)類對(duì)象沒有繼承自ContextBoundObject,則該對(duì)象默認(rèn)會(huì)創(chuàng)建和運(yùn)行在應(yīng)用程序域的default context中,而繼承自ContextBoundObject的類對(duì)象,在其對(duì)象實(shí)例被激活時(shí),CLR將自動(dòng)創(chuàng)建一個(gè)單獨(dú)的Context供其生存。

    如果需要判定ContextBoundObject類型對(duì)象所認(rèn)定的Context,只需要為該類型對(duì)象施加ContextAttribute即可。ContextAttribute類繼承了Attribute類,它是一個(gè)特殊的Attribute,通過它,可以獲得對(duì)象需要的合適的執(zhí)行環(huán)境,即Context(上下文)。同時(shí),ContextAttribute還實(shí)現(xiàn)了IContextAttribute和IContextProperty接口。

    由于在施加Attribute時(shí),只需要獲取ContextBoundObject類型的Context屬性,因此,我們也可以自定義Attribute,只需要該自定義的Attribute實(shí)現(xiàn)IContextAttribute即可。IContextAttribute接口的定義如下:
    public interface IContextAttribute
    {
        bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg);
        void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg);
    }

    每個(gè)context attribute在context的構(gòu)造階段(通常是由ContextBoundObject對(duì)象構(gòu)造動(dòng)作引發(fā)的)會(huì)被首先問到IsContextOK,就是說新創(chuàng)建的這個(gè)ContextBoundObjec(通過ctorMsg可以知道是哪個(gè)對(duì)象的哪個(gè)構(gòu)造方法被用來構(gòu)造ContextBoundObjec對(duì)象的)能不能在給定的ctx中存在?這個(gè)目的主要是減少應(yīng)用程序域中潛在的context的數(shù)量,如果某些ContextBoundObjec類型可以共用一個(gè)有所需特性的執(zhí)行環(huán)境的話,就可以不用再創(chuàng)建新的環(huán)境,而只要在已有的環(huán)境中構(gòu)造并執(zhí)行就好了。

    如果ContextBoundObjec類型上設(shè)置的所有context attributes都認(rèn)同給定的context(也即調(diào)用代碼所處的context)是正確地的(此時(shí)IsContextOK均返回true),那么新的ContextBoundObjec就會(huì)被綁定到這個(gè)context上。否則,只有有一個(gè)attribute返回false,就會(huì)立即創(chuàng)建一個(gè)新的context。然后,CLR會(huì)再一次詢問每一個(gè)context attribute新構(gòu)造的context是否正確,由于Context已經(jīng)被重新創(chuàng)建,通常此時(shí)返回的結(jié)果應(yīng)為false。那么,Context構(gòu)造程序就會(huì)調(diào)用其GetPropertiesForNewContext()方法,context attribute可以用這個(gè)方法傳入的構(gòu)造器方法調(diào)用信息(ctorMsg)中的context properties列表(ContextProperties)來為新建的context增加所需的context properties。

    從AOP的角度來看,Context類似于前面分析的橫切關(guān)注點(diǎn),那么利用我們自定義的Context Attribute,就可以獲得對(duì)象它所存在的上下文,從而建立業(yè)務(wù)對(duì)象與橫切關(guān)注點(diǎn)之間的關(guān)系。

    4.2.3代理(Proxy)
    在程序設(shè)計(jì)中使用代理(Proxy),最重要的目的是可以通過利用代理對(duì)象,實(shí)現(xiàn)代理所指向的真實(shí)對(duì)象的訪問。在GOF的《設(shè)計(jì)模式》中,將代理(Proxy)模式分為四種:
    1、遠(yuǎn)程代理(Remote Proxy)。它為一個(gè)位于不同的地址空間的對(duì)象提供一個(gè)局域代表對(duì)象。這個(gè)不同的地址空間可以是在本機(jī)器中,亦可是在另一臺(tái)機(jī)器中。
    2、虛代理(Virtual Proxy)。它能夠根據(jù)需要?jiǎng)?chuàng)建一個(gè)資源消耗較大的對(duì)象,使得此對(duì)象只在需要時(shí)才會(huì)被真正創(chuàng)建。
    3、保護(hù)代理(Protection Proxy)。它控制對(duì)原始對(duì)象的訪問,如果需要可以給不同的用戶提供不同級(jí)別的使用權(quán)限。
    4、智能引用代理(Smart Reference Proxy)。它取代了簡(jiǎn)單的指針,在訪問一個(gè)對(duì)象時(shí),提供一些額外的操作。例如,對(duì)指向?qū)嶋H對(duì)象的引用計(jì)數(shù),這樣當(dāng)該對(duì)象沒有引用時(shí),可以自動(dòng)釋放它。當(dāng)?shù)谝淮我靡粋€(gè)持久對(duì)象時(shí),智能引用可以將該對(duì)象裝入內(nèi)存。在訪問一個(gè)實(shí)際對(duì)象前,檢查該對(duì)象是否被鎖定,以確保其他對(duì)象不能改變它。

    在.Net Remoting中,采用了遠(yuǎn)程代理(Remote Proxy)模式。采用代理技術(shù),使得對(duì)象可以在兩個(gè)不同的應(yīng)用程序域(甚至可以是兩臺(tái)不同的機(jī)器)之間傳遞。代理在.Net中被分為透明代理(Transparent Proxy)和真實(shí)代理(Real Proxy)。Transparent Proxy的目標(biāo)是在 CLR 中在 IL 層面最大程度扮演被代理的遠(yuǎn)端對(duì)象,從類型轉(zhuǎn)換到類型獲取,從字段訪問到方法調(diào)用。對(duì) CLR 的使用者來說,Transparent Proxy和被其代理的對(duì)象完全沒有任何區(qū)別,只有通過 RemotingServices.IsTransparentProxy 才能區(qū)分兩者的區(qū)別。Real Proxy則是提供給 CLR 使用者擴(kuò)展代理機(jī)制的切入點(diǎn),通過從Real Proxy繼承并實(shí)現(xiàn) Invoke 方法,用戶自定義代理實(shí)現(xiàn)可以自由的處理已經(jīng)被從棧調(diào)用轉(zhuǎn)換為消息調(diào)用的目標(biāo)對(duì)象方法調(diào)用,如實(shí)現(xiàn)緩存、身份驗(yàn)證、安全檢測(cè)、延遲加載等等。

    如果我們希望自己定義的代理類能夠“模仿”真實(shí)對(duì)象的能力,首先就需要實(shí)現(xiàn)透明代理。然而,CLR中雖然提供了這樣一個(gè)透明代理類(_TransparentProxy),我們卻不能讓自己的代理類從透明代理類派生,也不能通過自定義Attribute、實(shí)現(xiàn)標(biāo)志性接口等方式將代理類標(biāo)識(shí)為透明代理,從而讓CLR能夠認(rèn)識(shí)。要獲取透明代理,必須要提供一個(gè)真實(shí)代理。一個(gè)真實(shí)代理是一個(gè)從System.Runtime.Remoting.Proxies.RealProxy派生而來的類。這個(gè)RealProxy類的首要功能就是幫我們?cè)谶\(yùn)行期動(dòng)態(tài)生成一個(gè)可以透明兼容于某一個(gè)指定類的透明代理類實(shí)例。從RealProxy的源代碼,可以看出透明代理和真實(shí)代理之間的關(guān)系:
    namespace System.Runtime.Remoting.Proxies
    {
      abstract public class RealProxy
      {
        protected RealProxy(Type classToProxy) : this(classToProxy, (IntPtr)0, null){}
        protected RealProxy(Type classToProxy, IntPtr stub, Object stubData)
        {
          if(!classToProxy.IsMarshalByRef && !classToProxy.IsInterface)
            throw new ArgumentException(…);

          if((IntPtr)0 == stub)
          {
            stub = _defaultStub;
            stubData = _defaultStubData;
          }

          _tp = null;

          if (stubData == null)
            throw new ArgumentNullException(”stubdata”);

          _tp = RemotingServices.CreateTransparentProxy(this, classToProxy, stub, stubData);
        }
        public virtual Object GetTransparentProxy()
        {
          return _tp;
        }
      }
    }

    很明顯,透明代理(Transparent Proxy)是在RealProxy類的構(gòu)造函數(shù)中,調(diào)用RemotingServices.CreateTransparentProxy()方法動(dòng)態(tài)創(chuàng)建的。CreateTransparentProxy()方法將把被代理的類型強(qiáng)制轉(zhuǎn)換為統(tǒng)一的由 CLR 在運(yùn)行時(shí)創(chuàng)建的 RuntimeType 類型,進(jìn)而調(diào)用 Internal 方法完成TransparentProxy的創(chuàng)建。通過GetTransparentProxy()方法,就可以獲得創(chuàng)建的這個(gè)透明代理對(duì)象。因此,要定義自己的真實(shí)代理對(duì)象,只需要繼承RealProxy類即可:
    using System.Runtime.Remoting.Proxies;

    public class MyRealProxy: RealProxy
    {
      public MyRealProxy(Type classToProxy): base(classToProxy)
      {
        …
      }
    }

    透明代理和真實(shí)代理在上下文(Context)中,會(huì)起到一個(gè)偵聽器的作用。首先,透明代理將調(diào)用堆棧序列化為一個(gè)稱為消息(Message)的對(duì)象,然后再將消息傳遞給真實(shí)代理。真實(shí)代理接收消息,并將其發(fā)送給第一個(gè)消息接收進(jìn)行處理。第一個(gè)消息接收對(duì)消息進(jìn)行預(yù)處理,將其繼續(xù)發(fā)送給位于客戶端和對(duì)象之間的消息接收堆棧中的下一個(gè)消息接收,然后對(duì)消息進(jìn)行后處理。下一個(gè)消息接收也如此照辦,以此類推,直到到達(dá)堆棧構(gòu)建器接收,它將消息反序列化回調(diào)用堆棧,調(diào)用對(duì)象,序列化出站參數(shù)和返回值,并返回到前面的消息接收。這個(gè)調(diào)用鏈如圖4.2所示。

    aop4.2.gif
    圖4.2 代理(Proxy)偵聽消息的順序

    由于透明代理完全等同于其代理的對(duì)象,因此,當(dāng)我們偵聽到代理對(duì)象被調(diào)用的消息時(shí),就可以截取該消息,并織入需要執(zhí)行的方面邏輯,完成橫切關(guān)注邏輯與核心邏輯的動(dòng)態(tài)代碼織入。

    4.3 .Net平臺(tái)下AOP技術(shù)實(shí)現(xiàn)
    4.3.1實(shí)現(xiàn)原理
    根據(jù)對(duì).Net中元數(shù)據(jù)(Metadata)、Attribute、上下文(Context)、代理(Proxy)等技術(shù)要素的分析,要在.Net中實(shí)現(xiàn)AOP,首先需要獲得一個(gè)類對(duì)象的上下文(Context),則其前提就是這個(gè)類必須從System.ContextBoundObject類派生。這個(gè)類對(duì)象就相當(dāng)于AOP中的核心關(guān)注點(diǎn),而類對(duì)象的上下文則屬于AOP的橫切關(guān)注點(diǎn)。很顯然,只需要利用上下文,就可以方便的實(shí)現(xiàn)核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)的分離。

    正如圖4.1所示,對(duì)象是存在于上下文中的。利用自定義Attribute,可以建立對(duì)象與上下文之間的關(guān)聯(lián)。Attribute可以擴(kuò)展對(duì)象的元數(shù)據(jù),從而標(biāo)識(shí)出該對(duì)象屬于其中的一個(gè)或多個(gè)Aspect。一旦該對(duì)象實(shí)例被創(chuàng)建或調(diào)用時(shí),就可以利用反射技術(shù)獲得該對(duì)象的自定義Attribute。為使得對(duì)象的元數(shù)據(jù)與上下文關(guān)聯(lián)起來,就要求這個(gè)自定義的Attribute必須實(shí)現(xiàn)接口IContextAttribute。

    獲得了對(duì)象的上下文之后,透明代理與真實(shí)代理就能夠?qū)υ搶?duì)象的方法調(diào)用(包括構(gòu)造函數(shù))進(jìn)行偵聽,并完成消息的傳遞。傳遞的消息可以被Aspect截取,同時(shí)利用真實(shí)代理,也可以完成對(duì)業(yè)務(wù)對(duì)象的Decorate,將Aspect邏輯注入到業(yè)務(wù)對(duì)象中。由于在大型的企業(yè)系統(tǒng)設(shè)計(jì)中,橫切關(guān)注點(diǎn)會(huì)包括事務(wù)管理、日志管理、權(quán)限控制等多方面,但由于方面(Aspect)在技術(shù)上的共同特性,我們可以利用.Net的相關(guān)技術(shù)實(shí)現(xiàn)方面(Aspect)的核心類庫,所有的橫切關(guān)注點(diǎn)邏輯,都可以定義為派生這些類庫的類型,從而真正在.Net中實(shí)現(xiàn)AOP技術(shù)。

    4.3.2 AOP公共類庫
    4.3.2.1 AOP Attribute
    如上所述,要實(shí)現(xiàn)AOP技術(shù),首先需要自定義一個(gè)Attribute。該自定義Attribute必須實(shí)現(xiàn)IContextAttribute,因此其定義如下所示:
    using System;
    using System.Runtime.Remoting.Contexts;
    using System.Runtime.Remoting.Activation;

    [AttributeUsage(AttributeTargets.Class)]
    public abstract class AOPAttribute:Attribute,IContextAttribute
    {
        private string m_AspectXml;
        private const string CONFIGFILE = @”configuration\aspect.xml”;
        public AOPAttribute()                    
        {
            m_AspectXml = CONFIGFILE;
        }   
        public AOPAttribute(string aspectXml)
        {
            this.m_AspectXml = aspectXml;
        }  
        protected abstract AOPProperty GetAOPProperty();

        #region IContextAttribute Members
        public sealed void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
        {
            AOPProperty property = GetAOPProperty();    
            property.AspectXml = m_AspectXml;     
            ctorMsg.ContextProperties.Add(property);
        }
        public bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
        {
            return false;
        }
    }

    類AOPAttribute除了繼承System.Attribute類之外,關(guān)鍵之處在于實(shí)現(xiàn)了接口IContextAttribute接口。接口方法GetPropertiesForNewContext()其功能是向Context添加屬性(Property)集合,這個(gè)集合是IConstructionCallMessage對(duì)象的ContextProperties屬性。而接口方法IsContextOK(),則用于判斷Context中是否存在指定的屬性。這個(gè)方法會(huì)在Context的構(gòu)造階段(通常是由被施加了AOPAttribute的業(yè)務(wù)對(duì)象在創(chuàng)建時(shí)引發(fā)的)被調(diào)用,如果返回false,會(huì)創(chuàng)建一個(gè)新的Context。

    GetAOPProperty()方法是一個(gè)受保護(hù)的抽象方法,繼承AOPAttribute的子類將重寫該方法,返回一個(gè)AOPProperty對(duì)象。在這里,我們利用了Template Method模式,通過該方法創(chuàng)建符合條件的AOPProperty對(duì)象,并被GetPropertiesForNewContext()方法添加到屬性集合中。

    抽象類AOPAttribute是所有與方面有關(guān)的Attribute的公共基類。所有方面的相關(guān)Attribute均繼承自它,同時(shí)實(shí)現(xiàn)GetAOPProperty()方法,創(chuàng)建并返回與之對(duì)應(yīng)的AOPProperty對(duì)象。

    4.3.2.2 AOP Property
    ContextProperties是一個(gè)特殊的集合對(duì)象,它存放的是對(duì)象被稱為Context Property,是一個(gè)實(shí)現(xiàn)了IContextProperty接口的對(duì)象,這個(gè)對(duì)象可以為相關(guān)的Context提供一些屬性。IContextProperty接口的定義如下:
    public interface IContextProperty
    {
        string Name { get; }
        bool IsNewContextOK(Context newCtx);
        void Freeze(Context newCtx);
    }

    IContextProperty接口的Name屬性,表示Context Property的名字,Name屬性值要求在整個(gè)Context中必須是唯一的。IsNewContextOK()方法用于確認(rèn)Context是否存在沖突的情況。而Freeze()方法則是通知Context Property,當(dāng)新的Context構(gòu)造完成時(shí),則進(jìn)入Freeze狀態(tài)(通常情況下,F(xiàn)reeze方法僅提供一個(gè)空的實(shí)現(xiàn))。

    由于IContextProperty接口僅僅是為Context提供一些基本信息,它并不能完成對(duì)方法調(diào)用消息的截取。根據(jù)對(duì)代理技術(shù)的分析,要實(shí)現(xiàn)AOP,必須在方法調(diào)用截取消息傳遞,并形成一個(gè)消息鏈Message Sink。因此,如果需要向所在的Context的Transparent Proxy/Real Proxy中植入Message Sink,Context Property還需要提供Sink的功能。所幸的是,.Net已經(jīng)提供了實(shí)現(xiàn)MessageSink功能的相關(guān)接口,這些接口的命名規(guī)則為IContributeXXXSink,XXX代表了四種不同的Sink:Envoy,ClientContext,ServerContext,Object。這四種接口有其相似之處,都只具有一個(gè)方法用于返回一個(gè)IMessageSink對(duì)象。由于我們需要獲取的透明代理對(duì)象,是能夠穿越不同的應(yīng)用程序域的。在一個(gè)應(yīng)用程序域收到其他應(yīng)用程序域的對(duì)象,則該對(duì)象在.Net中被稱為Server Object,該對(duì)象所處的Context也被稱為Server Context。我們?cè)?Net中實(shí)現(xiàn)AOP,其本質(zhì)正是要獲得對(duì)象的Server Context,并截取該Context中的方法調(diào)用消息,因而Context Property對(duì)象應(yīng)該實(shí)現(xiàn)IContributeServerContextSink接口。事實(shí)上,也只有IContributeServerContextSink接口的GetServerContextSink()方法,才能攔截包括構(gòu)造函數(shù)在內(nèi)的所有方法的調(diào)用。

    因此,AOP Property最終的定義如下:
    using System;
    using System.Runtime.Remoting.Activation;
    using System.Runtime.Remoting.Contexts;
    using System.Runtime.Remoting.Messaging;

    public abstract class AOPProperty : IContextProperty, IContributeServerContextSink
    {
        private string m_AspectXml;
        public AOPProperty()
        {
            m_AspectXml = string.Empty;          
        }
        public string AspectXml
        {
            set { m_AspectXml = value; }
        }
        protected abstract IMessageSink CreateAspect(IMessageSink nextSink);
        protected virtual string GetName()
        {
            return “AOP”;
        }
        protected virtual void FreezeImpl(Context newContext)
        {
            return;
        }
        protected virtual bool CheckNewContext(Context newCtx)
        {
            return true;
        }

        #region IContextProperty Members
        public void Freeze(Context newContext)
        {
            FreezeImpl(newContext);
        }
        public bool IsNewContextOK(Context newCtx)
        {
            return CheckNewContext(newCtx);
        }
        public string Name
        {
            get { return GetName(); }
        }
        #endregion

        #region IContributeServerContextSink Members
        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            Aspect aspect = (Aspect)CreateAspect(nextSink);           
            aspect.ReadAspect(m_AspectXml,Name);           
            return (IMessageSink)aspect;
        }
        #endregion
    }

    在抽象類AOPProperty中,同樣利用了Template Method模式,將接口IContextProperty的方法的實(shí)現(xiàn)利用受保護(hù)的虛方法延遲到繼承AOPProperty的子類中。同時(shí),對(duì)于接口IContributeServerContextSink方法GetServerContextSink(),則創(chuàng)建并返回了一個(gè)Aspect類型的對(duì)象,Aspect類型實(shí)現(xiàn)了IMessageSink接口,它即為AOP中的方面,是所有方面(Aspect)的公共基類。

    AOPProperty類作為抽象類,是所有與上下文有關(guān)的Property的公共基類。作為Context Property應(yīng)與Aspect相對(duì)應(yīng),且具體的AOPProperty類對(duì)象應(yīng)在AOPAttribute的子類中創(chuàng)建并獲得。

    4.3.2.3 Aspect與PointCut
    Aspect類是AOP的核心,它的本質(zhì)是一個(gè)Message Sink,代理正是通過它進(jìn)行消息的傳遞,并截獲方法間傳遞的消息。Aspect類實(shí)現(xiàn)了IMessageSink接口,其定義如下:
    public interface IMessageSink
    {
        IMessage SyncProcessMessage(IMessage msg);
        IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink);
        IMessageSink NextSink { get; }    
    }

    IMessageSink接口利用NextSink將多個(gè)Message Sink連接起來,以形成一個(gè)消息接收器鏈;而SyncProcessMessage()和AsyncProcessMessage()方法則分別用于同步和異步操作,它們?cè)谙鬟f的時(shí)候被調(diào)用。

    注意方法SyncProcessMessage()中的參數(shù),是一個(gè)IMessage接口類型的對(duì)象。在.Net中,IMethodCallMessage和IMethodReturnMessage接口均繼承自IMessage接口,前者是調(diào)用方法的消息,而后者則是方法被調(diào)用后的返回消息。利用這兩個(gè)接口對(duì)象,就可以獲得一個(gè)對(duì)象方法的切入點(diǎn)。因此,一個(gè)最簡(jiǎn)單的Aspect實(shí)現(xiàn)應(yīng)該如下:
        public abstract class Aspect : IMessageSink
        {     
            private IMessageSink m_NextSink;

            public AOPSink(IMessageSink nextSink)
            {
                m_NextSink = nextSink;        
            }
            public IMessageSink NextSink
            {
                get { return m_NextSink; }
            }
            public IMessage SyncProcessMessage(IMessage msg)
            {
                IMethodCallMessage call = msg as IMethodCallMessage;
                if (call == null)
                {
                     return null;
                }

                IMessage retMsg = null;
                BeforeProcess();
                retMsg = m_NextSink.SyncProcessMessage(msg);
                AfterProcess();
                return retMsg;
            }
            public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
            {
                 return null;
            }
            private void BeforeProcess()
            {
                 //方法調(diào)用前的實(shí)現(xiàn)邏輯;
            }
            private void AfterProcess()
            {
                 //方法調(diào)用后的實(shí)現(xiàn)邏輯;
            }
        }

    注意在方法SyncProcessMessage()中,IMessageSink對(duì)象m_NextSink通過Aspect構(gòu)造函數(shù)賦值為業(yè)務(wù)對(duì)象的透明代理,在調(diào)用m_NextSink的SyncProcessMessage()方法時(shí),此時(shí)調(diào)用的是與該透明代理對(duì)應(yīng)的真實(shí)代理。如果在消息接收鏈中還存在代理,在方法調(diào)用將會(huì)沿著消息鏈不斷的向后執(zhí)行。而對(duì)于一個(gè)業(yè)務(wù)對(duì)象而言,此時(shí)的IMessage即為該對(duì)象中被調(diào)用的方法,SyncProcessMessage(msg)就相當(dāng)于執(zhí)行了該方法。而在m_NextSink.SyncProcessMessage(msg)方法前后執(zhí)行BeforeProcess()和AfterProcess(),就完成了對(duì)方法的截取,并將自己的Aspect邏輯織入到業(yè)務(wù)對(duì)象的方法調(diào)用中,從而實(shí)現(xiàn)了AOP。

    然而對(duì)于AOP技術(shù)的實(shí)際應(yīng)用而言,并非業(yè)務(wù)對(duì)象的所有方法都需要被截取進(jìn)而進(jìn)行方面的織入。也即是說,切入點(diǎn)(PointCut)必須實(shí)現(xiàn)可被用戶定義。而所謂切入點(diǎn),實(shí)際上是業(yè)務(wù)對(duì)象方法與Advice之間的映射關(guān)系。在.Net中,我們可以通過集合對(duì)象來管理這個(gè)映射關(guān)系。由于Advice包括Before Advice和After Advice,因此,在Aspect類中應(yīng)該定義兩個(gè)集合對(duì)象:
    private SortedList m_BeforeAdvices;
    private SortedList m_AfterAdvices;

    在添加PointCut時(shí),是將方法名和具體的Advice對(duì)象建立映射,根據(jù)SortedList集合的特性,我們將方法名作為SortedList的Key,而Advice則作為SortedList的Value:
            protected virtual void AddBeforeAdvice(string methodName, IBeforeAdvice before)
            {
                lock (this.m_BeforeAdvices)
                {
                    if (!m_BeforeAdvices.Contains(methodName))
                    {
                        m_BeforeAdvices.Add(methodName, before);
                    }
                }
            }
            protected virtual void AddAfterAdvice(string methodName, IAfterAdvice after)
            {
                lock (this.m_AfterAdvices)
                {
                    if (!m_AfterAdvices.Contains(methodName))
                    {
                        m_AfterAdvices.Add(methodName, after);
                    }
                }
            }

    在向SortedList添加PointCut時(shí),需要先判斷集合中是否已經(jīng)存在該P(yáng)ointCut。同時(shí)考慮到可能存在并發(fā)處理的情況,在添加PointCut時(shí),利用lock對(duì)該操作進(jìn)行了加鎖,避免并發(fā)處理時(shí)可能會(huì)出現(xiàn)的錯(cuò)誤。

    建立了方法名和Advice的映射關(guān)系,在執(zhí)行SyncProcessMessage()方法,就可以根據(jù)IMessage的值,獲得業(yè)務(wù)對(duì)象被調(diào)用方法的相關(guān)屬性,然后根據(jù)方法名,找到其對(duì)應(yīng)的Advice,從而執(zhí)行相關(guān)的Advice代碼:
           public IMessage SyncProcessMessage(IMessage msg)
           {           
                 IMethodCallMessage call = msg as IMethodCallMessage;
                 string methodName = call.MethodName.ToUpper();
                 IBeforeAdvice before = FindBeforeAdvice(methodName);
                 if (before != null)
                 {
                      before.BeforeAdvice(call);
                 }           
                 IMessage retMsg = m_NextSink.SyncProcessMessage(msg);
                 IMethodReturnMessage reply = retMsg as IMethodReturnMessage;
                 IAfterAdvice after = FindAfterAdvice(methodName);
                 if (after != null)
                 {
                       after.AfterAdvice(reply);
                 }
                 return retMsg;
            }

    其中FindBeforeAdvice()和FindAfterAdvice()方法完成key和value的查找工作,分別的定義如下:
            public IBeforeAdvice FindBeforeAdvice(string methodName)
            {
                IBeforeAdvice before;
                lock (this.m_BeforeAdvices)
                {
                    before = (IBeforeAdvice)m_BeforeAdvices[methodName];
                }
                return before;
            }
            public IAfterAdvice FindAfterAdvice(string methodName)
            {
                IAfterAdvice after;
                lock (this.m_AfterAdvices)
                {
                    after = (IAfterAdvice)m_AfterAdvices[methodName];
                }
                return after;
            }

    在找到對(duì)應(yīng)的Advice對(duì)象后,就可以調(diào)用Advice對(duì)象的相關(guān)方法,完成方面邏輯代碼的織入。

    那么,PointCut是在什么時(shí)候添加的呢?我們可以在AOP的配置文件(Aspect.xml)中配置PointCut,然后在Aspect類中,通過ReadAspect()方法,讀入配置文件,獲取PointCut以及Aspect需要的信息,包括方法名和Advice對(duì)象(通過反射動(dòng)態(tài)創(chuàng)建),在執(zhí)行AddBeforeAdvice()和AddAfterAdvice()方法將PointCut添加到各自的集合對(duì)象中:
    public void ReadAspect(string aspectXml,string aspectName)
    {
        IBeforeAdvice before = (IBeforeAdvice)Configuration.GetAdvice(aspectXml,aspectName,Advice.Before);
        string[] methodNames = Configuration.GetNames(aspectXml,aspectName,Advice.Before);
        foreach (string name in methodNames)
        {
             AddBeforeAdvice(name,before);
        }
        IAfterAdvice after = (IAfterAdvice)Configuration.GetAdvice(aspectXml,aspectName,Advice.After);
        string[] methodNames = Configuration.GetNames(aspectXml,aspectName,Advice.After);
        foreach (string name in methodNames)
        {
             AddAfterAdvice(name,after);
        }   
    }

    一個(gè)Aspect的配置文件示例如下:
    <aop>
        <aspect value =”LogAOP”>
            <advice type=”before” assembly=”AOP.Advice” class=”AOP.Advice.LogAdvice”>
         <pointcut>ADD</pointcut>
         <pointcut>SUBSTRACT</pointcut>
     </advice>
     <advice type=”after” assembly=”AOP.Advice” class=”AOP.Advice.LogAdvice”>
         <pointcut>ADD</pointcut>
         <pointcut>SUBSTRACT</pointcut>
     </advice>
        </aspect> 
    </aop>

    配置文件中,元素Advice的assembly屬性和class屬性值,是利用反射創(chuàng)建Advice對(duì)象所需要的信息。另外,Aspect的名字應(yīng)與方面的Property名保持一致,因?yàn)镽eadAspect()方法是通過AOPProperty名字來定位配置文件的Aspect。

    4.3.2.4 Advice
    在Aspect類中,已經(jīng)使用了Advice對(duì)象。根據(jù)類別不同,這些Advice對(duì)象分別實(shí)現(xiàn)IBeforeAdvice接口和IAfterAdvice接口:
    using System;
    using System.Runtime.Remoting.Messaging;

    public interface IBeforeAdvice
    {
        void BeforeAdvice(IMethodCallMessage callMsg);
    }
    public interface IAfterAdvice
    {
        void AfterAdvice(IMethodReturnMessage returnMsg);
    }

    接口方法應(yīng)該實(shí)現(xiàn)具體的方面邏輯,同時(shí)可以通過IMethodCallMessage對(duì)象獲得業(yè)務(wù)對(duì)象的調(diào)用方法信息,通過IMethodReturnMessage對(duì)象獲得方法的返回信息。

    4.4 .Net平臺(tái)AOP技術(shù)應(yīng)用案例
    在4.3.2節(jié),我們已基本實(shí)現(xiàn)了AOP的公共類庫,這其中包括AOPAttribute,AOPProperty,Aspect,IBeforeAdvice,IAfterAdvice。根據(jù)這些公共基類或接口,我們就可以定義具體的方面,分別繼承或?qū)崿F(xiàn)這些類與接口。為了展示AOP在.Net中的應(yīng)用,在本節(jié),我將以一個(gè)簡(jiǎn)單的實(shí)例來說明。

    假定我們要設(shè)計(jì)一個(gè)計(jì)算器,它能提供加法和減法功能。我們希望,在計(jì)算過程中,能夠通過日志記錄整個(gè)計(jì)算過程及其結(jié)果,同時(shí)需要監(jiān)測(cè)其運(yùn)算性能。該例中,核心業(yè)務(wù)是加法和減法,而公共的業(yè)務(wù)則是日志與監(jiān)測(cè)功能。根據(jù)前面對(duì)AOP的分析,這兩個(gè)功能作為橫切關(guān)注點(diǎn),將是整個(gè)系統(tǒng)需要?jiǎng)冸x出來的“方面”。

    4.4.1日志方面
         作為日志方面,其功能就是要截取業(yè)務(wù)對(duì)象方法的調(diào)用,并獲取之間傳遞的消息內(nèi)容。從上節(jié)的分析我們知道,方法間的消息可以從IMethodCallMessage和IMethodReturnMessage接口對(duì)象獲得。因此,實(shí)現(xiàn)日志方面,最重要的是實(shí)現(xiàn)Aspect類中的SyncProcessMessage()方法。此外,也應(yīng)定義與之對(duì)應(yīng)的Attribute和Property,以及實(shí)現(xiàn)日志邏輯的Advice。

    4.4.1.1日志Attribute(LogAOPAttribute)
    LogAOPAttribute類繼承AOPAttribute,由于AOPAttribute類主要是創(chuàng)建并獲得對(duì)應(yīng)的AOPProperty,因此,其子類也僅需要重寫父類的受保護(hù)抽象方法GetAOPProperty()即可:
           [AttributeUsage(AttributeTargets.Class)]
           public class LogAOPAttribute:AOPAttribute
           {
                  public LogAOPAttribute():base()
                  {}
                  public LogAOPAttribute(string aspectXml):base(aspectXml)
                  {}

                  protected override AOPProperty GetAOPProperty()
                  {
                         return new LogAOPProperty();
                  }   
           }

    通過對(duì)GetAOPProperty()方法的重寫,創(chuàng)建并獲得了與LogAOPAttribute類相對(duì)應(yīng)的LogAOPProperty,此時(shí)在LogAOPAttribute所施加的業(yè)務(wù)對(duì)象的上下文中,所存在的AOP Property就應(yīng)該是具體的LogAOPProperty對(duì)象。

    4.4.1.2日志Property(LogAOPProperty)
    由于Context Property的名字在上下文中必須是唯一的,因此每個(gè)方面的Property的名字也必須是唯一的。因此在繼承AOPProperty的子類LogAOPProperty中,必須重寫父類的虛方法GetName(),同時(shí)在LogAOPProperty中,還應(yīng)該創(chuàng)建與之對(duì)應(yīng)的Aspect,也即是Message Sink,而這個(gè)工作是由抽象方法CreateAspect()來完成的。因此,LogAOPProperty類的定義如下:
           public class LogAOPProperty:AOPProperty
           {
                  protected override IMessageSink CreateAspect(IMessageSink nextSink)
                  {
                         return new LogAspect(nextSink);
                  }
                  protected override string GetName()
                  {
                         return “LogAOP”;
                  }
           }

    為避免Property的名字出現(xiàn)重復(fù),約定成俗以方面的Attribute名為Property的名字,以本例而言,其Property名為L(zhǎng)ogAOP。

    4.4.1.3日志Aspect(LogAspect)
    LogAspect完成的功能主要是將Advice與業(yè)務(wù)對(duì)象的方法建立映射,并將其添加到Advice集合中。由于我們?cè)贏OP實(shí)現(xiàn)中,利用了xml配置文件來配置PointCut,因此對(duì)于所有Aspect而言,這些操作都是相同的,只要定義了正確的配置文件,將其讀入即可。對(duì)于Aspect的SyncProcessMessage(),由于攔截和織入的方法是一樣的,不同的只是Advice的邏輯而已,因此在所有Aspect的公共基類中已經(jīng)提供了默認(rèn)的實(shí)現(xiàn):
           public class LogAspect:Aspect
           {
                  public LogAspect(IMessageSink nextSink):base(nextSink)
                  {}           
           }

    然后定義正確的配置文件:
    <aspect value =”LogAOP”>
        <advice type=”before” assembly=” AOP.Advice” class=”AOP.Advice.LogAdvice”>
            <pointcut>ADD</pointcut>
     <pointcut>SUBSTRACT</pointcut>
        </advice>
        <advice type=”after” assembly=” AOP.Advice” class=”AOP.Advice.LogAdvice”>
     <pointcut>ADD</pointcut>
     <pointcut>SUBSTRACT</pointcut>
        </advice>
    </aspect>

    LogAdvice所屬的程序集文件為AOP.Advice.dll,完整的類名為AOP.Advice.LogAdvice。

    4.4.1.4日志Advice(LogAdvice)
    由于日志方面需要記錄方法調(diào)用前后的相關(guān)數(shù)據(jù),因此LogAdvice應(yīng)同時(shí)實(shí)現(xiàn)IBeforeAdvice和IAfterAdvice接口:
        public class LogAdvice:IAfterAdvice,IBeforeAdvice
        {
            #region IBeforeAdvice Members
            public void BeforeAdvice(IMethodCallMessage callMsg)
            {
                if (callMsg == null)
                {
                    return;
                }
                Console.WriteLine(”{0}({1},{2})”, callMsg.MethodName, callMsg.GetArg(0), callMsg.GetArg(1));
            }
            #endregion

            #region IAfterAdvice Members
            public void AfterAdvice(IMethodReturnMessage returnMsg)
            {
                if (returnMsg == null)
                {
                    return;
                }
                Console.WriteLine(”Result is {0}”, returnMsg.ReturnValue);
            }
            #endregion
        }

    在BeforeAdvice()方法中,消息類型為IMethodCallMessage,通過這個(gè)接口對(duì)象,可以獲取方法名和方法調(diào)用的參數(shù)值。與之相反,AfterAdvice()方法中的消息類型為IMethodReturnMessage,Advice所要獲得的數(shù)據(jù)為方法的返回值ReturnValue。

    4.4.2性能監(jiān)測(cè)方面
    性能監(jiān)測(cè)方面與日志方面的實(shí)現(xiàn)大致相同,為簡(jiǎn)便起見,我要實(shí)現(xiàn)的性能監(jiān)測(cè)僅僅是記錄方法調(diào)用前和調(diào)用后的時(shí)間。

    4.4.2.1性能監(jiān)測(cè)Attribute(MonitorAOPAttribute)
    與日志Attribute相同,MonitorAOPAttribute僅僅需要?jiǎng)?chuàng)建并返回對(duì)應(yīng)的MonitorAOPProperty對(duì)象:
          [AttributeUsage(AttributeTargets.Class)]
          public class MonitorAOPAttribute:AOPAttribute
          {
                 public MonitorAOPAttribute():base()
                 {}
                 public MonitorAOPAttribute(string aspectXml):base(aspectXml)
                 {}
                 protected override AOPProperty GetAOPProperty()
                 {
                        return new MonitorAOPProperty();
                 } 
          }

    4.4.2.2性能監(jiān)測(cè)Property(MonitorAOPProperty)
    MonitorAOPProperty的屬性名將定義為MonitorAOP,使其與日志方面的屬性區(qū)別。除定義性能監(jiān)測(cè)方面的屬性名外,還需要重寫CreateAspect()方法,創(chuàng)建并返回對(duì)應(yīng)的方面對(duì)象MonitorAspect:
          public class MonitorAOPProperty:AOPProperty
          {
                 protected override IMessageSink CreateAspect(IMessageSink nextSink)
                 {
                        return new MonitorAspect(nextSink);
                 }
                 protected override string GetName()
                 {
                        return “MonitorAOP”;
                 }
          }

    4.4.2.3性能監(jiān)測(cè)Aspect(MonitorAspect)
    MonitorAspect類的實(shí)現(xiàn)同樣簡(jiǎn)單:
        public class MonitorAspect:Aspect
        {
                 public MonitorAspect(IMessageSink nextSink):base(nextSink)
                 {}
        }

    而其配置文件的定義則如下所示:
    <aspect value =”MonitorAOP”>
        <advice type=”before” assembly=” AOP.Advice” class=”AOP.Advice.MonitorAdvice”>
            <pointcut>ADD</pointcut>
     <pointcut>SUBSTRACT</pointcut>
        </advice>
        <advice type=”after” assembly=” AOP.Advice” class=”AOP.Advice.MonitorAdvice”>
     <pointcut>ADD</pointcut>
     <pointcut>SUBSTRACT</pointcut>
        </advice>
    </aspect> 

    MonitorAdvice所屬的程序集文件為AOP.Advice.dll,完整的類名為AOP.Advice.MonitorAdvice。

    4.4.2.4性能監(jiān)測(cè)Advice(MonitorAdvice)
    由于性能監(jiān)測(cè)方面需要記錄方法調(diào)用前后的具體時(shí)間,因此MonitorAdvice應(yīng)同時(shí)實(shí)現(xiàn)IBeforeAdvice和IAfterAdvice接口:
        public class MonitorAdvice : IBeforeAdvice, IAfterAdvice
        {
            #region IBeforeAdvice Members
            public void BeforeAdvice(IMethodCallMessage callMsg)
            {
                if (callMsg == null)
                {
                    return;
                }
                Console.WriteLine(”Before {0} at {1}”, callMsg.MethodName, DateTime.Now);
            }
            #endregion

            #region IAfterAdvice Members
            public void AfterAdvice(IMethodReturnMessage returnMsg)
            {
                if (returnMsg == null)
                {
                    return;
                }
                Console.WriteLine(”After {0} at {1}”, returnMsg.MethodName, DateTime.Now);
            }
            #endregion
        }

    MonitorAdvice只需要記錄方法調(diào)用前后的時(shí)間,因此只需要分別在BeforeAdvice()和AfterAdvice()方法中,記錄當(dāng)前的時(shí)間即可。

    4.4.3業(yè)務(wù)對(duì)象與應(yīng)用程序
    4.4.3.1業(yè)務(wù)對(duì)象(Calculator)
    通過AOP技術(shù),我們已經(jīng)將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)完全分離,我們?cè)诙x業(yè)務(wù)對(duì)象時(shí),并不需要關(guān)注包括日志、性能監(jiān)測(cè)等方面,這也是AOP技術(shù)的優(yōu)勢(shì)。當(dāng)然,由于要利用.Net中的Attribute及代理技術(shù),對(duì)于施加了方面的業(yè)務(wù)對(duì)象而言,仍然需要一些小小的限制。

    首先,我們應(yīng)該將定義好的方面Aspect施加給業(yè)務(wù)對(duì)象。其次,由于代理技術(shù)要獲取業(yè)務(wù)對(duì)象的上下文(Context),該上下文必須是指定的,而非默認(rèn)的上下文。上下文的獲得,是在業(yè)務(wù)對(duì)象創(chuàng)建和調(diào)用的時(shí)候,如果要獲取指定的上下文,在.Net中,要求業(yè)務(wù)對(duì)象必須繼承ContextBoundObject類。因此,最后業(yè)務(wù)對(duì)象Calculator類的定義如下所示:
           [MonitorAOP]
           [LogAOP]
           public class Calculator : ContextBoundObject
           {
                  public int Add(int x,int y)
                  {
                         return x + y;
                  }
                  public int Substract(int x,int y)
                  {
                         return x - y;
                  }
           }

    [MonitorAOP]和[LogAOP]正是之前定義的方面Attribute,此外Calculator類繼承了ContextBoundObject。除此之外,Calculator類的定義與普通的對(duì)象定義無異。然而,正是利用AOP技術(shù),就可以攔截Calculator類的Add()和Substract()方法,對(duì)其進(jìn)行日志記錄和性能監(jiān)測(cè)。而實(shí)現(xiàn)日志記錄和性能監(jiān)測(cè)的邏輯代碼,則完全與Calculator類的Add()和Substract()方法分開,實(shí)現(xiàn)了兩者之間依賴的解除,有利于模塊的重用和擴(kuò)展。

    4.4.3.2應(yīng)用程序(Program)
    我們可以實(shí)現(xiàn)簡(jiǎn)單的應(yīng)用程序,來看看業(yè)務(wù)對(duì)象Calculator施加了日志方面和性能檢測(cè)方面的效果:
          class Program
          {          
                 [STAThread]
                 static void Main(string[] args)
                 {
                        Calculator cal = new Calculator();
                        cal.Add(3,5);
                        cal.Substract(3,5);
                        Console.ReadLine();
                 }
          }

    程序創(chuàng)建了一個(gè)Calculator對(duì)象,同時(shí)調(diào)用了Add()和Substract()方法。由于Calculator對(duì)象被施加了日志方面和性能檢測(cè)方面,因此運(yùn)行結(jié)果會(huì)將方法調(diào)用的詳細(xì)信息和調(diào)用前后的運(yùn)行當(dāng)前時(shí)間打印出來,如圖4.3所示:

    aop4.3.gif
    圖4.3 施加了方面的業(yè)務(wù)對(duì)象調(diào)用結(jié)果

    如果要改變記錄日志和性能監(jiān)測(cè)結(jié)果的方式,例如將其寫到文件中,則只需要改變LogAdvice和MonitorAdvice的實(shí)現(xiàn),對(duì)于Calculator對(duì)象而言,則不需要作任何改變

    轉(zhuǎn)貼來自:http://www.cnblogs.com/wayne-ivan/archive/2006/09/07/496906.html

    posted on 2007-05-30 11:39 ★yesjoy★ 閱讀(520) 評(píng)論(0)  編輯  收藏 所屬分類: AOP(面向方面編程)
    主站蜘蛛池模板: 亚洲最大天堂无码精品区| 亚洲丝袜美腿视频| 亚洲片一区二区三区| 区久久AAA片69亚洲| 五月天网站亚洲小说| 秋霞人成在线观看免费视频 | 久久久久久久久久久免费精品| 在线免费观看h片| 日本最新免费网站| 精品亚洲永久免费精品| 久久国产精品免费| 久久永久免费人妻精品下载| 国产卡二卡三卡四卡免费网址| 男人的天堂亚洲一区二区三区| 久久精品国产精品亚洲| 亚洲黄色在线视频| 日本系列1页亚洲系列| 免费无码又爽又刺激网站直播| 国产免费久久精品99re丫y| 亚洲高清偷拍一区二区三区| 亚洲国产精品婷婷久久| 亚洲国产日韩a在线播放| 三年片免费观看大全国语| 无码人妻一区二区三区免费手机| 亚洲第一页日韩专区| 亚洲综合小说久久另类区| 国产精品亚洲一区二区三区久久| 好紧我太爽了视频免费国产| 午夜神器成在线人成在线人免费| 在线亚洲97se亚洲综合在线| 亚洲av无码一区二区三区观看| aaa毛片视频免费观看| 最近中文字幕mv免费高清视频7| 日本亚洲免费无线码| 夜色阁亚洲一区二区三区| 久久久久亚洲AV无码专区首JN| 欧美亚洲国产SUV| 伊人久久免费视频| jjzz亚洲亚洲女人| 亚洲乱码一二三四区乱码| 亚洲精品国产首次亮相|