日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

AOP技术研究 再续

發(fā)布時間:2023/12/9 编程问答 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AOP技术研究 再续 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

第四部分? .Net平臺AOP技術(shù)研究

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

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

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

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

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

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

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

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

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

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

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

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

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

1. 自描述文件

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

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

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

3.Attribute

.NET Framework允許在編譯文件中聲明特定種類的元數(shù)據(jù)(稱為Attribute)。在整個 .NET Framework 中到處都可以發(fā)現(xiàn)Attribute的存在,Attribute用于更精確地控制運(yùn)行時程序如何工作。另外,用戶可以通過自定義屬性向 .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)。每個元數(shù)據(jù)表都保留有關(guān)程序元素的信息。例如,一個元 數(shù)據(jù)表說明代碼中的類,另一個元數(shù)據(jù)表說明字段等。如果您的代碼中有10個類,類表將有10行,每行為1個類。元數(shù)據(jù)表引用其他的表和堆。例如,類的元數(shù) 據(jù)表引用方法表。元數(shù)據(jù)以四種堆結(jié)構(gòu)存儲信息:字符串、Blob、用戶字符串和 GUID。所有用于對類型和成員進(jìn)行命名的字符串都存儲在字符串堆中。例如,方法表不直接存儲特定方法的名稱,而是指向存儲在字符串堆中的方法的名稱。

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

最高位字節(jié) (0x06) 表示這是一個MethodDef標(biāo)記。低位的三個字節(jié) (000004) 指示公共語言運(yùn)行庫在 MethodDef 表的第四行查找對該方法定義進(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)行時的作用
由于在MSIL指令中包含了元數(shù)據(jù)標(biāo)記,因此,當(dāng)公共語言運(yùn)行庫(CLR)將代碼加載到內(nèi)存時,將向元 數(shù)據(jù)咨詢該代碼模塊中包含的信息。運(yùn)行庫對Microsoft 中間語言 (MSIL) 流執(zhí)行廣泛的分析,將其轉(zhuǎn)換為快速本機(jī)指令。運(yùn)行庫根據(jù)需要使用實(shí)時 (JIT) 編譯器將 MSIL 指令轉(zhuǎn)換為本機(jī)代碼,每次轉(zhuǎn)換一個方法。例如,有一個類APP,其中包含了Main()方法和Add()方法:

C#語言:? 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 編譯器讀取整個方法的 MSIL,對其進(jìn)行徹底地分析,然后為該方法生成有效的本機(jī)指令。在 IL_000d 遇到 Add 方法 (/* 06000003 */) 的元數(shù)據(jù)標(biāo)記,運(yùn)行庫使用該標(biāo)記參考 MethodDef 表的第三行。

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

?

?

?

?

相對虛擬地址 (RVA) ?

?

?
ImplFlags ?

?

?
Flags ?

?

?
Name ?

?

?(指向字符串堆) ? ?
Signature ?

?

?(指向 Blob 堆) ? ?

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

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

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

4.2.2 Attribute
4.2.2.1 Attribute概述

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

Attribute可應(yīng)用于任何目標(biāo)元素,我們可以通過AttributeTargets枚舉指定其施加的目標(biāo),AttributeTargets枚舉在.Net中的定義如下:

C#語言: 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)行多個值的組合,如果你沒有指定參數(shù),那么默認(rèn)參數(shù)就是All 。

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

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

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

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

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

通過自定義Attribute,可使得用戶自定義的信息與Attribute施加的類本身相關(guān)聯(lián)。例如,給定一個自定義的 .NET 屬性,我們就可以輕松地將調(diào)用跟蹤Attribute與類的方法相關(guān)聯(lián):

C#語言: public class Bar
{
??? [CallTracingAttribute("In Bar ctor")]
??? public Bar() {}
??? [CallTracingAttribute("In Bar.Calculate method")]
??? public int Calculate(int x, int y){ return x + y; }
}

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

C#語言:? 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信息,如:

C#語言: 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),是指一個邏輯上的執(zhí)行環(huán)境。每一個應(yīng)用程序域都有一個或多個Context,.Net中的所有對象都會在相應(yīng)的Context中創(chuàng)建和運(yùn)行。如圖4.1所示,它顯示了一個安全地存在于Context的對象:

?

圖4.1 安全地存在于Context的對象

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

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

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

C#語言:? public interface IContextAttribute
{
??? bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg);
??? void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg);
}

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

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

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

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

在.Net Remoting中,采用了遠(yuǎn)程代理(Remote Proxy)模式。采用代理技術(shù),使得對象可以在兩個不同的應(yīng)用程序域(甚至可以是兩臺不同的機(jī)器)之間傳遞。代理在.Net中被分為透明代理 (Transparent Proxy)和真實(shí)代理(Real Proxy)。Transparent Proxy的目標(biāo)是在 CLR 中在 IL 層面最大程度扮演被代理的遠(yuǎn)端對象,從類型轉(zhuǎn)換到類型獲取,從字段訪問到方法調(diào)用。對 CLR 的使用者來說,Transparent Proxy和被其代理的對象完全沒有任何區(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)對象方法調(diào)用,如實(shí)現(xiàn)緩存、身份驗(yàn)證、安全檢測、延遲加載等等。

如果我們希望自己定義的代理類能夠“模仿”真實(shí)對象的能力,首先就需要實(shí)現(xiàn)透明代理。然而,CLR中雖然提供了這樣一個透明代理類 (_TransparentProxy),我們卻不能讓自己的代理類從透明代理類派生,也不能通過自定義Attribute、實(shí)現(xiàn)標(biāo)志性接口等方式將代理 類標(biāo)識為透明代理,從而讓CLR能夠認(rèn)識。要獲取透明代理,必須要提供一個真實(shí)代理。一個真實(shí)代理是一個從 System.Runtime.Remoting.Proxies.RealProxy派生而來的類。這個RealProxy類的首要功能就是幫我們在運(yùn) 行期動態(tài)生成一個可以透明兼容于某一個指定類的透明代理類實(shí)例。從RealProxy的源代碼,可以看出透明代理和真實(shí)代理之間的關(guān)系:

C#語言:? 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()方法動態(tài) 創(chuàng)建的。CreateTransparentProxy()方法將把被代理的類型強(qiáng)制轉(zhuǎn)換為統(tǒng)一的由 CLR 在運(yùn)行時創(chuàng)建的 RuntimeType 類型,進(jìn)而調(diào)用 Internal 方法完成TransparentProxy的創(chuàng)建。通過GetTransparentProxy()方法,就可以獲得創(chuàng)建的這個透明代理對象。因此,要定 義自己的真實(shí)代理對象,只需要繼承RealProxy類即可:

C#語言: using System.Runtime.Remoting.Proxies;

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

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

?

圖4.2 代理(Proxy)偵聽消息的順序

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

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

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

獲得了對象的上下文之后,透明代理與真實(shí)代理就能夠?qū)υ搶ο蟮姆椒ㄕ{(diào)用(包括構(gòu)造函數(shù))進(jìn)行偵聽,并完成消息的傳遞。傳遞的消息可以被Aspect 截取,同時利用真實(shí)代理,也可以完成對業(yè)務(wù)對象的Decorate,將Aspect邏輯注入到業(yè)務(wù)對象中。由于在大型的企業(yè)系統(tǒng)設(shè)計中,橫切關(guān)注點(diǎn)會包括 事務(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ù),首先需要自定義一個Attribute。該自定義Attribute必須實(shí)現(xiàn)IContextAttribute,因此其定義如下所示:

C#語言: 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)集合,這個集合是 IConstructionCallMessage對象的ContextProperties屬性。而接口方法IsContextOK(),則用于判斷 Context中是否存在指定的屬性。這個方法會在Context的構(gòu)造階段(通常是由被施加了AOPAttribute的業(yè)務(wù)對象在創(chuàng)建時引發(fā)的)被調(diào) 用,如果返回false,會創(chuàng)建一個新的Context。

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

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

4.3.2.2 AOP Property
ContextProperties是一個特殊的集合對象,它存放的是對象被稱為Context Property,是一個實(shí)現(xiàn)了IContextProperty接口的對象,這個對象可以為相關(guān)的Context提供一些屬性。 IContextProperty接口的定義如下:

C#語言: public interface IContextProperty
{
??? string Name { get; }
??? bool IsNewContextOK(Context newCtx);
??? void Freeze(Context newCtx);
}

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

由于IContextProperty接口僅僅是為Context提供一些基本信息,它并不能完成對方法調(diào)用消息的截取。根據(jù)對代理技術(shù)的分析,要 實(shí)現(xiàn)AOP,必須在方法調(diào)用截取消息傳遞,并形成一個消息鏈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。這四種接口有其相似之處,都只具有一個方法用于返回一個 IMessageSink對象。由于我們需要獲取的透明代理對象,是能夠穿越不同的應(yīng)用程序域的。在一個應(yīng)用程序域收到其他應(yīng)用程序域的對象,則該對象 在.Net中被稱為Server Object,該對象所處的Context也被稱為Server Context。我們在.Net中實(shí)現(xiàn)AOP,其本質(zhì)正是要獲得對象的Server Context,并截取該Context中的方法調(diào)用消息,因而Context Property對象應(yīng)該實(shí)現(xiàn)IContributeServerContextSink接口。事實(shí)上,也只有 IContributeServerContextSink接口的GetServerContextSink()方法,才能攔截包括構(gòu)造函數(shù)在內(nèi)的所有方 法的調(diào)用。

因此,AOP Property最終的定義如下:

C#語言:? 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的子類中。同時,對于接口 IContributeServerContextSink方法GetServerContextSink(),則創(chuàng)建并返回了一個Aspect類型的對 象,Aspect類型實(shí)現(xiàn)了IMessageSink接口,它即為AOP中的方面,是所有方面(Aspect)的公共基類。

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

4.3.2.3 Aspect與PointCut
Aspect類是AOP的核心,它的本質(zhì)是一個Message Sink,代理正是通過它進(jìn)行消息的傳遞,并截獲方法間傳遞的消息。Aspect類實(shí)現(xiàn)了IMessageSink接口,其定義如下:

C#語言:? public interface IMessageSink
{
??? IMessage SyncProcessMessage(IMessage msg);
??? IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink);
??? IMessageSink NextSink { get; }???
}

IMessageSink接口利用NextSink將多個Message Sink連接起來,以形成一個消息接收器鏈;而SyncProcessMessage()和AsyncProcessMessage()方法則分別用于同步和異步操作,它們在消息傳遞的時候被調(diào)用。

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

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

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

C#語言:? private SortedList m_BeforeAdvices;
private SortedList m_AfterAdvices;

在添加PointCut時,是將方法名和具體的Advice對象建立映射,根據(jù)SortedList集合的特性,我們將方法名作為SortedList的Key,而Advice則作為SortedList的Value:

C#語言:? 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時,需要先判斷集合中是否已經(jīng)存在該P(yáng)ointCut。同時考慮到可能存在并發(fā)處理的情況,在添加PointCut時,利用lock對該操作進(jìn)行了加鎖,避免并發(fā)處理時可能會出現(xiàn)的錯誤。

建立了方法名和Advice的映射關(guān)系,在執(zhí)行SyncProcessMessage()方法,就可以根據(jù)IMessage的值,獲得業(yè)務(wù)對象被調(diào)用方法的相關(guān)屬性,然后根據(jù)方法名,找到其對應(yīng)的Advice,從而執(zhí)行相關(guān)的Advice代碼:

C#語言:? 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的查找工作,分別的定義如下:

C#語言:? 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;
}

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

那么,PointCut是在什么時候添加的呢?我們可以在AOP的配置文件(Aspect.xml)中配置PointCut,然后在Aspect類 中,通過ReadAspect()方法,讀入配置文件,獲取PointCut以及Aspect需要的信息,包括方法名和Advice對象(通過反射動態(tài)創(chuàng) 建),在執(zhí)行AddBeforeAdvice()和AddAfterAdvice()方法將PointCut添加到各自的集合對象中:

C#語言:? 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);
??? }??
}

一個Aspect的配置文件示例如下:

XML語言:? <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對象所需要的信息。另外,Aspect的名字 應(yīng)與方面的Property名保持一致,因?yàn)镽eadAspect()方法是通過AOPProperty名字來定位配置文件的Aspect。

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

C#語言:? 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)具體的方面邏輯,同時可以通過IMethodCallMessage對象獲得業(yè)務(wù)對象的調(diào)用方法信息,通過IMethodReturnMessage對象獲得方法的返回信息。

4.4 .Net平臺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é),我將以一個簡單的實(shí)例來說明。

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

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

4.4.1.1日志Attribute(LogAOPAttribute)
LogAOPAttribute類繼承AOPAttribute,由于AOPAttribute類主要是創(chuàng)建并獲得對應(yīng)的AOPProperty,因此,其子類也僅需要重寫父類的受保護(hù)抽象方法GetAOPProperty()即可:

C#語言:? [AttributeUsage(AttributeTargets.Class)]
public class LogAOPAttribute:AOPAttribute
{
?????? public LogAOPAttribute():base()
?????? {}
?????? public LogAOPAttribute(string aspectXml):base(aspectXml)
?????? {}

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

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

4.4.1.2日志Property(LogAOPProperty)
由于Context Property的名字在上下文中必須是唯一的,因此每個方面的Property的名字也必須是唯一的。因此在繼承AOPProperty的子類 LogAOPProperty中,必須重寫父類的虛方法GetName(),同時在LogAOPProperty中,還應(yīng)該創(chuàng)建與之對應(yīng)的Aspect, 也即是Message Sink,而這個工作是由抽象方法CreateAspect()來完成的。因此,LogAOPProperty類的定義如下:

C#語言:? 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名為LogAOP。

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

C#語言: public class LogAspect:Aspect
{
?????? public LogAspect(IMessageSink nextSink):base(nextSink)
?????? {}??????????
}

然后定義正確的配置文件:

XML語言: <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í)現(xiàn)IBeforeAdvice和IAfterAdvice接口:

C#語言:? 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,通過這個接口對象,可以獲取方法名和方法調(diào)用的參數(shù)值。 與之相反,AfterAdvice()方法中的消息類型為IMethodReturnMessage,Advice所要獲得的數(shù)據(jù)為方法的返回值 ReturnValue。

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

4.4.2.1性能監(jiān)測Attribute(MonitorAOPAttribute)
與日志Attribute相同,MonitorAOPAttribute僅僅需要創(chuàng)建并返回對應(yīng)的MonitorAOPProperty對象:

C#語言:? [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)測Property(MonitorAOPProperty)
MonitorAOPProperty的屬性名將定義為MonitorAOP,使其與日志方面的屬性區(qū)別。除定義性能監(jiān)測方面的屬性名外,還需要重寫CreateAspect()方法,創(chuàng)建并返回對應(yīng)的方面對象MonitorAspect:

C#語言:? 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)測Aspect(MonitorAspect)
MonitorAspect類的實(shí)現(xiàn)同樣簡單:

C#語言:? public class MonitorAspect:Aspect
{
???????? public MonitorAspect(IMessageSink nextSink):base(nextSink)
???????? {}
}

而其配置文件的定義則如下所示:

XML語言:? <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)測Advice(MonitorAdvice)
由于性能監(jiān)測方面需要記錄方法調(diào)用前后的具體時間,因此MonitorAdvice應(yīng)同時實(shí)現(xiàn)IBeforeAdvice和IAfterAdvice接口:

C#語言:? 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)用前后的時間,因此只需要分別在BeforeAdvice()和AfterAdvice()方法中,記錄當(dāng)前的時間即可。

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

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

C#語言: [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類的定義與普通的對象定義無異。然而,正是利用AOP技術(shù),就可以攔截 Calculator類的Add()和Substract()方法,對其進(jìn)行日志記錄和性能監(jiān)測。而實(shí)現(xiàn)日志記錄和性能監(jiān)測的邏輯代碼,則完全與 Calculator類的Add()和Substract()方法分開,實(shí)現(xiàn)了兩者之間依賴的解除,有利于模塊的重用和擴(kuò)展。

4.4.3.2應(yīng)用程序(Program)
我們可以實(shí)現(xiàn)簡單的應(yīng)用程序,來看看業(yè)務(wù)對象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)建了一個Calculator對象,同時調(diào)用了Add()和Substract()方法。由于Calculator對象被施加了日志方面和性能檢測方面,因此運(yùn)行結(jié)果會將方法調(diào)用的詳細(xì)信息和調(diào)用前后的運(yùn)行當(dāng)前時間打印出來,如圖4.3所示:

?

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

如果要改變記錄日志和性能監(jiān)測結(jié)果的方式,例如將其寫到文件中,則只需要改變

總結(jié)

以上是生活随笔為你收集整理的AOP技术研究 再续的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

最新国产精品拍自在线播放 | 亚洲激情综合网 | 黄色精品一区二区 | 午夜丁香视频在线观看 | 伊人天堂av | 六月丁香激情综合色啪小说 | 成人午夜免费福利 | 麻豆国产精品视频 | 国产免费看| 国产精品网址在线观看 | 精品亚洲va在线va天堂资源站 | 91中文在线视频 | 麻豆免费在线播放 | 青春草视频在线播放 | 美女视频a美女大全免费下载蜜臀 | av高清在线观看 | 又爽又黄在线观看 | 亚洲午夜电影网 | 国产黄在线看 | www.av在线.com| 国产精品国产三级国产aⅴ9色 | 免费观看国产精品 | 国产精品黄色影片导航在线观看 | 亚洲日本一区二区在线 | 天天射夜夜爽 | sesese图片| 国产做a爱一级久久 | 91精品国产九九九久久久亚洲 | 特黄一级毛片 | 国内综合精品午夜久久资源 | 999视频在线播放 | 亚洲一区视频免费观看 | 91在线视频在线观看 | 91精品免费在线观看 | 国产视频1区2区3区 久久夜视频 | 国产在线精品观看 | 国产精品a成v人在线播放 | 激情久久网| 91精品久久香蕉国产线看观看 | 色综合激情久久 | 五月花婷婷 | 免费毛片aaaaaa | 狠狠的日 | 在线视频精品播放 | 欧美网址在线观看 | 国产亚洲精品久 | 日狠狠| 亚洲精品在线免费观看视频 | 欧美午夜精品久久久久久孕妇 | 免费一级黄色 | 麻豆国产露脸在线观看 | 女人魂免费观看 | 日韩专区在线 | 欧美日韩午夜 | 久久综合九色综合97_ 久久久 | 91香蕉视频黄色 | 天天射天天干天天操 | 中文字幕视频一区二区 | 视频在线观看亚洲 | 国产一区二区精品 | 亚洲精品欧洲精品 | bbbbb女女女女女bbbbb国产 | 特级西西www44高清大胆图片 | 成人9ⅰ免费影视网站 | 九草在线视频 | 精品99久久 | 色姑娘综合天天 | 亚洲伦理精品 | 亚州成人av在线 | 久久久午夜影院 | 99 色| 狠狠干网址 | 在线视频精品播放 | 伊人电影在线观看 | 久久久免费看片 | 日韩超碰 | 日韩肉感妇bbwbbwbbw | 中文字幕在线观看完整版 | 97超碰超碰久久福利超碰 | 1024在线看片 | 国产亚洲免费的视频看 | 国产精品门事件 | 日韩有码在线观看视频 | 久久五月天色综合 | 国产网红在线观看 | 五月天网站在线 | 精品国产一区二区三区久久久蜜月 | 日本精品二区 | 国产精品久久久久久久久久 | 91av观看 | 欧美日韩另类在线观看 | 欧美一级专区免费大片 | 久久少妇免费视频 | 在线观看亚洲成人 | 久久99久久精品 | 国产精品久久久久久久久岛 | 777奇米四色| 久草免费福利在线观看 | 亚洲激情五月 | 久久99在线 | 精品一区精品二区 | 天天操综 | 在线观看日韩免费视频 | 国产福利免费看 | 国产成人久久77777精品 | 天天爽夜夜爽人人爽一区二区 | av黄色免费看 | 欧美另类交在线观看 | av在线电影网站 | 国产精品热视频 | 精品久久久影院 | 亚洲一区二区三区毛片 | 国产丝袜一区二区三区 | 国内精品亚洲 | 5月丁香婷婷综合 | 一区二区中文字幕在线观看 | 丁香综合激情 | 久久免费公开视频 | 丁香在线观看完整电影视频 | 久久久久久久久久久久久久免费看 | 免费看的国产视频网站 | 精品久久久久久久久久久久久 | 狠狠色伊人亚洲综合成人 | 五月婷婷综合在线视频 | 天堂av官网| 午夜色站 | 国产成人在线网站 | 最新av网站在线观看 | 在线看v片成人 | 欧美污网站 | 国产福利91精品一区 | 一区二区三区手机在线观看 | 国产精品久久久久久久久久免费 | 国产精品国内免费一区二区三区 | 亚洲精品一区二区久 | 欧美亚洲成人免费 | av免费线看 | 欧美日韩精品区 | www.五月天激情 | 五月天色中色 | 国产精品久久久久国产精品日日 | 国产日韩精品欧美 | 久草在线91 | 美女免费网站 | 国产一区在线视频观看 | 在线免费观看成人 | 婷婷综合激情 | 国产中文字幕一区二区 | 天天干天天看 | 精品福利视频在线 | 色99在线 | 欧美日韩免费网站 | 91福利影院在线观看 | 伊人影院99 | 久久久精品免费观看 | 成年人网站免费在线观看 | 久久精品国产免费 | 激情综合网五月激情 | 亚洲精品中文字幕在线观看 | 国产第一页福利影院 | 99精品免费久久久久久久久 | 97超碰在线免费 | 在线观看国产成人av片 | 亚洲视频在线免费看 | 欧美91精品久久久久国产性生爱 | 欧美日韩高清在线 | 亚洲电影毛片 | av亚洲产国偷v产偷v自拍小说 | 91手机在线看片 | 国产福利精品在线观看 | 在线色亚洲 | 99久久精品电影 | 激情婷婷欧美 | 少妇精品久久久一区二区免费 | 97视频播放 | 黄色福利网 | 日韩精品一区不卡 | 99c视频高清免费观看 | 久久99网| 丁香激情综合国产 | 欧美成人h版电影 | 亚洲一区久久 | 天天插天天射 | 国产色在线,com | 亚洲精品国产综合久久 | 日日噜噜噜噜夜夜爽亚洲精品 | 久久精品99国产精品日本 | 久久久久国产精品视频 | 成人h在线 | 久久一区二区免费视频 | 欧美日韩国产色综合一二三四 | 免费裸体视频网 | 色中文字幕在线观看 | 1区2区3区在线观看 三级动图 | 91在线视频 | 国产成人精品一区二区三区免费 | 丁香婷婷色综合亚洲电影 | 久草在线观看 | 久久免费av | 韩国在线一区二区 | 丁香导航| 日日天天av | 激情偷乱人伦小说视频在线观看 | 人人爱爱 | av丁香| 亚洲在线网址 | 伊人五月婷 | 日韩午夜三级 | 欧美天天干 | 精品久久久久一区二区国产 | 久久九九国产精品 | 深夜免费福利在线 | 中文字幕欧美激情 | 成人免费网站视频 | 四虎影视4hu4虎成人 | 成人久久国产 | 免费美女久久99 | 久久99精品久久久久久 | 精品国产观看 | av不卡网站| 欧美色图狠狠干 | 久久精品麻豆 | 中文字幕中文字幕在线中文字幕三区 | 精品国产免费av | 在线免费试看 | 久久艹国产视频 | 狠狠色综合欧美激情 | 97在线观看免费视频 | 丁香六月婷婷开心 | 久久五月情影视 | 国产在线视频一区二区 | 久久国产女人 | 99精品国产免费久久久久久下载 | 蜜桃av久久久亚洲精品 | 四虎小视频 | 五月天婷婷在线视频 | 亚洲国产精品va在线看黑人 | 视频在线观看国产 | 久久久久国产精品厨房 | 久久艹艹 | 性日韩欧美在线视频 | 国产99久久久久 | 日韩久久精品一区二区 | x99av成人免费 | 黄色福利| 在线视频在线观看 | 81国产精品久久久久久久久久 | 亚洲成人精品影院 | 久久99最新地址 | 国产免费人人看 | 五月天久久精品 | 日本色小说视频 | 亚洲精品视频在线播放 | 性色xxxxhd | 亚洲人成在线观看 | 中文字幕一区二区三区四区在线视频 | 久久成人国产精品入口 | 夜夜视频 | 美女网站黄在线观看 | 97超碰资源网 | 三上悠亚一区二区在线观看 | 99 精品 在线| 久久综合中文色婷婷 | 精品国产一区二区三区久久影院 | 亚洲精品中文字幕在线观看 | 在线精品观看 | a特级毛片 | 91天堂影院| 一级黄色片网站 | 欧美日韩亚洲在线观看 | 国产精品高清av | 国产精品国产三级国产aⅴ无密码 | 国产丝袜美腿在线 | 91在线观看欧美日韩 | 国产精品中文字幕在线观看 | 久久中文字幕导航 | av理论电影| 五月综合激情婷婷 | 欧美一级黄大片 | 亚洲精品播放 | 亚洲欧美va| www.97色.com| av免费网 | 91精品国自产在线观看欧美 | 日本黄色免费看 | 成年人在线免费看片 | 超碰免费观看 | av高清网站在线观看 | 四虎影视精品永久在线观看 | 亚洲综合在线一区二区三区 | 天天亚洲综合 | 在线三级中文 | 国产精品麻豆99久久久久久 | 亚洲免费色 | av在线电影网站 | 狠狠色网 | 成年人免费观看在线视频 | 国产精品久久久久9999吃药 | 亚洲精品在线视频播放 | 日日婷婷夜日日天干 | 99视频久久| 国产精品99久久久久的智能播放 | 激情综合色图 | 日韩免费看视频 | 精品一区二区久久久久久久网站 | 97视频免费在线观看 | 免费在线国产精品 | 99欧美| 久在线 | 免费精品久久久 | 成人在线视频一区 | 欧美日韩一级视频 | 四虎影视成人精品国库在线观看 | 中文在线字幕观看电影 | 日韩激情在线 | 久草在线免费资源 | 91av视屏 | 欧美日韩国产免费视频 | 在线 精品 国产 | 日日干日日 | 免费看一级特黄a大片 | 中文字幕一区二区三区久久 | 97国产大学生情侣白嫩酒店 | 国产视频精选 | 国产一级久久 | a级国产乱理论片在线观看 伊人宗合网 | 久草免费在线视频观看 | 在线成人一区二区 | 亚洲欧美日韩国产一区二区 | 在线观看免费国产小视频 | av片一区二区 | 97天堂 | 日韩电影在线观看中文字幕 | h动漫中文字幕 | 免费亚洲一区二区 | 国产精品av免费在线观看 | 在线观看中文字幕网站 | 久久九九久久精品 | www日| 1区2区3区在线观看 三级动图 | 91精品爽啪蜜夜国产在线播放 | 综合国产视频 | 人人天天夜夜 | 麻豆视频国产在线观看 | 中文字幕在线观看免费观看 | av高清一区二区三区 | 色99久久| 亚洲3级 | 久草在线免 | 色婷婷播放 | 91av色 | 欧美一区二区在线刺激视频 | 狠狠干中文字幕 | 91福利小视频 | 51久久夜色精品国产麻豆 | 五月天激情综合 | 综合网在线视频 | 伊人亚洲精品 | 国产又粗又猛又黄又爽视频 | 2023亚洲精品国偷拍自产在线 | 午夜手机看片 | 国产日本在线观看 | 激情视频免费观看 | 国产 日韩 欧美 中文 在线播放 | 天天操天天曰 | 国产精品免费看 | 久久手机看片 | 国产色在线 | japanesexxxxfreehd乱熟 | 日日干干夜夜 | 国产精品免费久久久久 | 精品视频在线视频 | 九九久久影院 | 亚洲免费小视频 | 人人爱爱人人 | 婷婷综合影院 | 成人久久影院 | 友田真希x88av | 久久国语露脸国产精品电影 | 免费观看一级视频 | 久久婷婷精品视频 | 中文字幕在线观看视频一区 | 日韩av一区在线观看 | av在线最新 | 日本动漫做毛片一区二区 | 中文字幕在线视频一区二区三区 | 欧美精品久久久久久 | 久久精品日本啪啪涩涩 | 久久激情视频免费观看 | 亚洲在线精品 | 久久免费播放视频 | 久久精品视频国产 | 日韩电影中文字幕在线 | 欧美精品一区二区三区四区在线 | 99久久99久国产黄毛片 | www.亚洲激情.com | 国产免费人人看 | av在线看网站 | 国产精品久久久久国产精品日日 | 久久精品一区八戒影视 | 国产成人精品女人久久久 | 99热 精品在线 | 国产91精品一区二区绿帽 | 人人干在线观看 | 免费黄色av | 在线高清 | 国产一及片 | 久久久久久久影视 | 久久国产精品网站 | 成年人国产视频 | 日韩免费福利 | 国产一区 在线播放 | 久久高清国产 | 成人97人人超碰人人99 | 最新av网址在线 | 99精品视频在线看 | 久久免费国产视频 | 亚洲三区在线 | 99re8这里有精品热视频免费 | 美女国内精品自产拍在线播放 | 粉嫩av一区二区三区入口 | 福利二区视频 | 久久久久久久久久久成人 | 国产精品一区二区免费在线观看 | 亚洲伊人天堂 | 天天干天天操天天射 | 国产九色在线播放九色 | 亚洲免费a | 狠狠狠狠狠狠狠狠干 | 免费污片 | 中国一级片免费看 | 九色视频网 | 91免费观看 | 久久精品系列 | 国内精品国产三级国产aⅴ久 | 免费h精品视频在线播放 | 日日操天天爽 | 狠狠色噜噜狠狠 | 婷婷丁香视频 | 亚洲人在线视频 | 精品av网站| 欧美巨大荫蒂茸毛毛人妖 | 亚洲精品中文在线观看 | 99精品免费| 国产在线视频一区二区 | 一级片免费观看 | 99精品国自产在线 | 色五月色开心色婷婷色丁香 | 国产高清视频免费最新在线 | 免费成人在线观看 | 成人av片在线观看 | 久久久久综合精品福利啪啪 | 日日夜夜精品免费 | 狠狠干婷婷 | 狠狠操狠狠插 | www.eeuss影院av撸 | 狠狠干狠狠色 | 美女视频久久久 | 免费av的网站| 在线中文字幕一区二区 | 欧美另类视频 | 精品v亚洲v欧美v高清v | 欧美日韩综合在线 | 99亚洲国产精品 | av一区二区三区在线 | 久久高清视频免费 | 国产精品久久久av | 337p日本欧洲亚洲大胆裸体艺术 | 亚洲五月综合 | 色综合天天狠天天透天天伊人 | 97视频人人 | 久久手机免费视频 | 免费色视频 | 99精品免费久久久久久日本 | 91av视频网| 99精品视频在线播放免费 | 五月婷综合 | 成人免费亚洲 | 国产精品亚洲片夜色在线 | 五月婷婷,六月丁香 | 一级片视频在线 | 手机在线观看国产精品 | 日韩高清免费无专码区 | 麻豆免费视频网站 | 欧美精品免费在线 | 免费在线一区二区 | 欧美日韩在线视频一区 | 日韩精品中文字幕在线 | 久久亚洲专区 | 日韩电影黄色 | 国产成人av | 97精品国产一二三产区 | 天天拍天天操 | 深爱综合网| 免费在线h | 国产亚洲va综合人人澡精品 | 婷香五月 | 国产香蕉视频在线观看 | 国产午夜不卡 | 超碰在线最新地址 | 久久免费美女视频 | 天天操操操操操操 | 免费涩涩网站 | 欧美日韩国产精品一区二区三区 | 国产黄色特级片 | 天天草网站 | 日韩成片 | 久久91久久久久麻豆精品 | 国产在线观看,日本 | 香蕉视频导航 | 国产原创在线 | 黄色免费网站下载 | 久草免费在线视频观看 | 国产精品成人免费一区久久羞羞 | 日韩精品一区二区三区不卡 | 黄色网www| 国产资源中文字幕 | 久久爱www. | 91精品欧美| 天天综合色网 | 日本在线观看视频一区 | 亚洲国产精选 | 蜜臀aⅴ精品一区二区三区 久久视屏网 | 中文字幕免费 | 三级黄在线 | 在线免费观看视频一区二区三区 | 久草在线视频免费资源观看 | 中文字幕在线免费播放 | 国产在线观看一 | 99r精品视频在线观看 | 国产免费观看视频 | 毛片随便看| 欧美在线aa | 亚洲人成在线观看 | 欧美亚洲专区 | 国产一在线精品一区在线观看 | 又黄又爽又色无遮挡免费 | 国产精品麻| 18久久久| 天天综合久久 | 亚洲禁18久人片 | www.色国产 | 又爽又黄在线观看 | 伊人婷婷综合 | 国产精品电影一区二区 | 久久99婷婷 | 国产涩涩在线观看 | 欧美有色| 夜色资源站wwwcom | 成人免费视频免费观看 | 97香蕉久久国产在线观看 | 狠狠狠色丁香综合久久天下网 | 免费69视频| av高清网站在线观看 | 色噜噜噜噜 | 国产日女人 | 国产精品 欧美 日韩 | 最近更新中文字幕 | 欧美日韩在线电影 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 欧洲亚洲激情 | 美女视频黄免费 | 丁香六月激情 | 国产精品欧美精品 | 中文字幕久久精品一区 | 精品自拍sae8—视频 | 精品国产三级 | 黄色网址中文字幕 | 人人舔人人爱 | 在线va视频 | 在线观看的av网站 | 91 中文字幕 | 亚洲一区免费在线 | 天天插天天干天天操 | 521色香蕉网站在线观看 | 日韩精品一区二区不卡 | 国产三级国产精品国产专区50 | 天天干天天在线 | 久久艹免费 | 久久久久视| 色婷婷骚婷婷 | 国内三级在线观看 | 日韩黄色大片在线观看 | 99热九九这里只有精品10 | 五月婷婷伊人网 | 国产69久久久欧美一级 | 日本成人中文字幕在线观看 | www视频免费在线观看 | 一区二区三区 亚洲 | 玖玖玖在线 | 正在播放 国产精品 | 91福利在线导航 | 国产玖玖在线 | 日本超碰在线 | 激情av一区二区 | 日韩精品1区2区 | 九草在线观看 | 国产精品18久久久久久久久久久久 | 在线 日韩 av| 久久精品看| 97爱爱爱 | 91av色| 中文字幕在线观看免费高清完整版 | 四虎成人精品在永久免费 | 国产va在线| 欧美日韩国产精品一区二区亚洲 | 精品久久久精品 | 91在线看片| 人人爽人人片 | www.看片网站 | 日韩免费电影在线观看 | 久久公开视频 | 成年人电影免费在线观看 | 色网站中文字幕 | 夜夜嗨av色一区二区不卡 | 岛国一区在线 | 亚洲精品99久久久久中文字幕 | 日韩伦理片一区二区三区 | 久久精品国产免费观看 | 国产成人一区二区三区 | 免费a v在线 | 丰满少妇在线观看资源站 | 欧美精品网站 | 国产一级精品绿帽视频 | 午夜色大片在线观看 | 91欧美国产 | 最新日本中文字幕 | www.人人草| av中文在线 | 久久免费视频4 | 黄污视频网站 | 日本xxxxav | 中文字幕日韩国产 | 精品一区二区三区久久久 | 91精品国产综合久久婷婷香蕉 | 日韩一区二区免费视频 | 五月激情亚洲 | 国产亚洲成av片在线观看 | 亚洲视频六区 | 日韩色一区二区三区 | 久久国语| 在线中文字幕一区二区 | 欧美日韩高清国产 | 蜜桃av观看 | 国产成人一区二区三区影院在线 | 国产91精品高清一区二区三区 | 麻豆精品在线视频 | 国产一区二区不卡视频 | 九九久久免费 | 三级黄色欧美 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 中文av资源站 | 日韩色爱 | 久久艹影院 | 五月天婷婷视频 | 热久久免费视频精品 | av在线亚洲天堂 | 婷婷色九月 | 久久99精品热在线观看 | 日韩三级视频在线观看 | 国产69精品久久app免费版 | 国产福利精品一区二区 | 国产伦理剧 | 中文字幕精品视频 | 欧美无极色| 日本黄色免费网站 | 亚洲自拍自偷 | 成人黄色av免费在线观看 | 在线观看日韩精品视频 | 四虎国产精品成人免费影视 | www.夜夜操.com| 久久久国产精品久久久 | 麻豆影视在线免费观看 | 国产99一区二区 | 91一区二区在线 | 中文字幕一区二区三区在线播放 | 高清日韩一区二区 | 亚洲精品在线电影 | 精品国产一二三 | 成人av亚洲 | 国产精品av一区二区 | 中文字幕91 | 日本中文字幕影院 | 国产精品成人自拍 | 久久久久免费网站 | 97超碰中文字幕 | 国产正在播放 | 黄色天堂在线观看 | 欧美日韩不卡一区二区 | 午夜黄色 | 看av免费网站| 日日干网址 | 日韩成人看片 | 中国美女一级看片 | 久久久久国产成人免费精品免费 | 欧美日韩在线观看一区 | av网在线观看 | 久久免费高清视频 | 天天操天天干天天玩 | 午夜丰满寂寞少妇精品 | 成人av观看| 国产一级黄色片免费看 | 亚洲综合少妇 | 日韩在线视 | 日韩精品视频在线观看免费 | 又黄又爽免费视频 | 欧美日韩中文字幕在线视频 | 中文字幕在线播出 | 蜜臀久久99精品久久久无需会员 | 中文字幕免| 九九在线精品视频 | 在线免费黄色av | 久久伊人精品一区二区三区 | 亚洲一区欧美精品 | 干综合网| 中日韩欧美精彩视频 | 成人免费观看网站 | 免费av片在线 | 久久综合导航 | 丁香六月天 | 国产在线播放一区二区 | av免费电影在线观看 | 99热都是精品 | mm1313亚洲精品国产 | 天天插伊人 | 免费网站在线观看人 | 久久久久福利视频 | 97色婷婷人人爽人人 | 三级在线视频观看 | 国产99久久久精品视频 | 婷婷性综合 | 欧美一级视频在线观看 | 四虎国产永久在线精品 | 一区二区中文字幕在线播放 | 久久高清视频免费 | 国产自制av| 国产一级在线观看视频 | 欧美激情操 | 成人禁用看黄a在线 | 色偷偷人人澡久久超碰69 | 亚洲日本一区二区在线 | 99精品视频在线观看视频 | 国产原厂视频在线观看 | 国产一级大片免费看 | 伊人开心激情 | 91精品国产高清自在线观看 | 成片视频在线观看 | 日韩欧美69| 中文字幕999| 99精品99 | 8x成人免费视频 | 99色免费 | 青青河边草免费观看完整版高清 | 国产做a爱一级久久 | 97成人在线观看视频 | 久久久91精品国产一区二区精品 | 黄色com | 国产精品精品久久久 | 少妇啪啪av入口 | 在线看片成人 | 丁香久久五月 | 久久久精品一区二区 | 我爱av激情网 | 天天躁天天躁天天躁婷 | 国产精品毛片久久久久久 | 国产精品永久 | 91成人免费在线 | 国产成人av | 九九九九九九精品 | 91tv国产成人福利 | 欧美日韩一级久久久久久免费看 | 久久久高清免费视频 | 五月婷婷国产 | 国产午夜在线观看 | 国产黄色一级大片 | 色播五月激情综合网 | 天天综合狠狠精品 | 狠狠操天天操 | 国产免费观看高清完整版 | 色婷婷丁香 | 久久蜜臀一区二区三区av | 国产 一区二区三区 在线 | 五月天精品视频 | 久久电影日韩 | 2024国产在线| a色视频 | 亚洲精品影视 | 免费久草视频 | 激情综合国产 | 婷婷六月综合网 | 久久久精品视频网站 | 欧美成人精品xxx | 日韩一级电影在线观看 | 中文字幕人成一区 | 精品一区二区在线看 | 国产小视频福利在线 | 激情 婷婷 | 婷婷六月综合亚洲 | 亚洲五月激情 | 国产精品第一页在线 | 久久成人在线视频 | 国产视频一区二区三区在线 | 天天干天天干天天操 | 最近日本中文字幕a | 最近中文字幕免费大全 | 亚洲精品国产综合久久 | 亚洲一二视频 | 国产精品一区二区久久精品爱涩 | 天天插伊人 | 久久国产美女视频 | 91cn国产在线 | 日韩高清在线不卡 | 免费国产亚洲视频 | 久久久久久久免费观看 | a级国产乱理论片在线观看 特级毛片在线观看 | 日韩av区 | 久久五月婷婷丁香社区 | 免费黄色网址大全 | 国产精品a成v人在线播放 | 久久综合九色99 | 国产人成在线视频 | 久久欧洲视频 | 国产精品视频久久 | 97精品国产一二三产区 | 色成人亚洲网 | 久久久久久久久久久久99 | 91福利区一区二区三区 | 精品在线免费观看 | 欧美日本日韩aⅴ在线视频 插插插色综合 | 香蕉视频网址 | 国产成人在线观看免费 | 色开心| 欧美久草网 | 婷婷精品国产一区二区三区日韩 | 亚洲色图 校园春色 | 国产小视频网站 | 国产理论免费 | 97人人艹 | www国产亚洲 | 国产视频一区在线免费观看 | 国产露脸91国语对白 | 婷婷激情综合五月天 | 日韩视频1| 中文字幕久久久精品 | 天天干夜夜夜操天 | 深夜免费福利网站 | 免费看一级片 | 国语精品免费视频 | 中文字幕在线观 | 国产精品18久久久久久久久久久久 | 久久久久久伊人 | 91久久久久久国产精品 | 中文字幕乱在线伦视频中文字幕乱码在线 | 欧美激情在线看 | 婷婷久久国产 | 亚洲理论片在线观看 | www久久久久 | 久久久毛片 | 国模精品在线 | 日韩在线视频网 | 人人澡人人添人人爽一区二区 | 国产精品久久久久久久久久东京 | 婷婷丁香六月 | 视频国产一区二区三区 | 国产精品久久99综合免费观看尤物 | 人人爽影院 | 国产中文伊人 | 亚洲国产精品成人女人久久 | 日韩在线播放欧美字幕 | 亚州欧美视频 | 欧美日韩中文在线观看 | 久久午夜电影网 | 射久久| 中文字幕在线观看第一页 | 超碰免费公开 | 亚洲欧洲av在线 | 在线播放视频一区 | 91精品国产入口 | 香蕉蜜桃视频 | 国产日韩精品一区二区三区在线 | 国产精品一区二区免费在线观看 | 国产精品一区二区三区四区在线观看 | 久草精品视频在线观看 | 在线观看视频中文字幕 | 日日夜夜狠狠 | 不卡视频国产 | 丁五月婷婷 | 中文字幕在线观 | 九九热视频在线播放 | 国产在线观看你懂的 | 免费成人结看片 | 欧美福利视频 | 欧美一级大片在线观看 | 伊人久久精品久久亚洲一区 | 五月婷婷播播 | 国产二区视频在线观看 | 九九电影在线 | 五月婷丁香网 | 国内精品久久久久久久久久清纯 | 欧美一区二区三区在线 | 超碰在线天天 | 91免费日韩 | 欧美老女人xx | 五月开心六月伊人色婷婷 | 91传媒激情理伦片 | 欧美a级在线免费观看 | 在线观看亚洲 | 免费看的毛片 | 91免费网址 | 国产精品久久久久一区二区国产 | 精品视频99| 日韩免费在线一区 | 天天se天天cao天天干 | 手机在线永久免费观看av片 | www日韩视频 | 深夜免费网站 | 三上悠亚一区二区在线观看 | 亚洲电影一级黄 | 国产美女在线精品免费观看 | 国产无遮挡猛进猛出免费软件 | 亚洲精品女人久久久 | 亚洲www天堂com | 五月天激情婷婷 | 国产黄色大片 | 日本中文字幕免费观看 | 深夜免费福利在线 | 99精品系列 | 免费在线观看成人 | 国产高清视频色在线www | 精品视频在线播放 | 六月丁香伊人 | 国产精品不卡视频 | 国产精品视频永久免费播放 | 天天摸天天舔天天操 | 色婷婷在线视频 | 亚洲精品视频二区 | 丁香婷婷激情啪啪 | 在线精品亚洲 | 天天综合网~永久入口 | 91探花在线视频 | 久草精品视频在线观看 | 免费黄色小网站 | 在线观看精品一区 | 中文字幕在线免费观看 | 黄色电影在线免费观看 | 亚洲精品玖玖玖av在线看 | 天天搞天天干天天色 | www视频在线免费观看 | www.成人精品 | 国产精品久久久久影院 | 国产精品久久在线观看 | 久久99国产视频 | 国产玖玖精品视频 | 在线观看视频精品 | 国产中文字幕在线免费观看 | 福利视频一区二区 | 91精品一区二区在线观看 | 亚洲精品乱码久久久久久9色 | 国产一区二区在线播放视频 | 亚洲一区二区麻豆 | 欧美精品在线观看免费 | 国产激情电影综合在线看 | 日韩一级片观看 | 亚洲一区二区精品视频 | 成年人免费电影在线观看 | 中国一级片在线播放 | 久久久久久久久久久久久久电影 | 日日干视频 | 天天干天天天天 | 色噜噜色噜噜 | 国产999在线观看 | 97av在线视频 | 天天碰天天操 | 狠狠色丁香九九婷婷综合五月 | 天天弄天天干 | 2020天天干天天操 | 久久歪歪 | 999ZYZ玖玖资源站永久 | 国产做a爱一级久久 | 五月婷久 | 日韩精品视频免费看 | 亚洲黄色成人网 | 在线观看 国产 | 日韩理论在线 | 精油按摩av | 日韩大片免费在线观看 | 欧美久久久久久久久久 | 视频一区二区三区视频 | 超碰人人乐 | 色婷婷骚婷婷 | 国产生活一级片 | 在线91观看 | 天天天色综合 | 欧美激情片在线观看 | 黄av资源 | 黄色一级在线免费观看 | 日韩av福利在线 | 一二三区视频在线 |