【转】在.Net中关于AOP的实现
原文地址:http://www.uml.org.cn/net/201004213.asp
一、AOP實現初步
AOP將軟件系統分為兩個部分:核心關注點和橫切關注點。核心關注點更多的是Domain Logic,關注的是系統核心的業務;而橫切關注點雖與核心的業務實現無關,但它卻是一種更Common的業務,各個關注點離散地分布于核心業務的多處。這意味著,如果不應用AOP,那么這些橫切關注點所代表的業務代碼,就會分散在系統各處,導致系統中的每個模塊都與這些業務具有很強的依賴性。在這里,所謂橫切關注點所代表的業務,即為“方面(Aspect)”,常見的包括權限控制、日志管理、事務處理等等。
?????????????????????????
以權限控制為例,假設一個電子商務系統,需要對訂單管理用戶進行權限判定,只有系統用戶才能添加、修改和刪除訂單,那么傳統的設計方法是:
public class OrderManager
{
private ArrayList m_Orders;
public OrderManager()
{
?????? m_Orders = new ArrayList();
}
public void AddOrder(Order order)
{
??? if (permissions.Verify(Permission.ADMIN))
??? {
????????????? m_Orders.Add(order);
??? }
}
public void RemoveOrder(Order order)
{
??? if (permissions.Verify(Permission.ADMIN))
??? {
????????????? m_Orders.Remove(order);
??? }
}
}
這樣的設計其缺陷是將訂單管理業務與權限管理完全結合在一起,耦合度高。而在一個系統中,類似的權限控制會很多,這些代碼就好像一顆顆毒瘤一般蔓延于系統中的各處,一旦需要擴展,則給程序員們帶來的困難是不可估量的。
?????????????????????????
讓我們來觀察一下訂單管理業務中的權限管理。不管是添加訂單,還是刪除訂單,有關權限管理的內容是完全相同的。那么,為什么我們不能將這些相同的業務,抽象為一個對象,并將其從訂單管理業務中完全剝離出來呢?在傳統的OO設計思想,這種設想是不能實現的。因為訂單管理業務作為一個類對象,它封裝了諸如添加、刪除訂單等行為。這種封裝性,就決定了我們不可能切入到對象內部,通過獲取方法消息的形式,對對象行為進行監控與操作。
?????????????????????????
AOP的思想解決了這個問題,之所以稱為“方面(Aspect)”,就是把這些對象剖開,僅獲取其內部相一致的邏輯,并剝離出來,以“方面”的形式存在。要讓這些方面能夠對核心業務進行控制,就需要有一套獲取方法消息的機制。在.Net中,其中一種技術稱為動態代理。
?????????????????????????
在.Net中,要實現動態代理,需要用到.Net Remoting中的消息機制,以及.Net Framework內部提供的ContextAttribute類來自定義自己的Attribute。另外,.Net還要求調用“Aspect”的核心業務類,必須繼承ContextBoundObject類。只有這樣,我們才能截取其內部傳遞的方法消息。以下,是相關接口和類的說明。
ContextAttribute類?????????????????????????? ????????????????????????
該類繼承了Attribute類,它是一個特殊的Attribute,通過它,可以獲得對象需要的合適的執行環境,即Context(上下文)。它還實現了IContextAttribute和IContextProperty接口。我們自定義的Attribute將從ContextAttribute類派生。
構造函數:?????????????????????????? ????????????????????????
ContextAttribute類的構造函數帶有一個參數,用來設置ContextAttribute的名稱。?????????????????????????? ????????????????????????
公共屬性:?????????????????????????? ????????????????????????
Name:只讀屬性。返回ContextAttribute的名稱?????????????????????????? ????????????????????????
公共方法:?????????????????????????? ????????????????????????
GetPropertiesForNewContext:虛擬方法。向新的Context添加屬性集合。?????????????????????????? ????????????????????????
IsContextOK:虛擬方法。查詢客戶Context中是否存在指定的屬性。?????????????????????????? ????????????????????????
IsNewContextOK:虛擬方法。默認返回true。一個對象可能存在多個Context,使用這個方法來檢查新的Context中屬性是否存在沖突。?????????????????????????? ????????????????????????
Freeze:虛擬方法。該方法用來定位被創建的Context的最后位置。?????????????????????????? ????????????????????????
?????????????????????????
ContextBoundObject類?????????????????????????? ????????????????????????
這個類的對象通過Attribute來指定它所在的Context,凡是進入該Context的調用都可以被攔截。該類從MarshalByRefObject派生。?????????????????????????? ????????????????????????
?????????????????????????
IMessage:定義了被傳送的消息的實現。一個消息必須實現這個接口。?????????????????????????? ????????????????????????
?????????????????????????
IMessageSink:定義了消息接收器的接口,一個消息接收器必須實現這個接口。
該接口主要提供了兩個方法,分別進行同步和異步操作:
SyncProcessMessage(IMessage msg):接口方法,當消息傳遞的時候,該方法被調用;
AsyncProcessMessage(IMessage msg, IMessageSink replySink):該方法用于異步處理;
?????????????????????????
下面是實現權限控制AOP的簡單實現,首先我們自定義一個Attribute,它繼承了ContextAttribute:
[AttributeUsage(AttributeTargets.Class)]?????????????????????????????? ????????????????????????
??? public class AOPAttribute:ContextAttribute???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? public AOPAttribute()?????????????????????????????? ????????????????????????
??????????? : base("AOP")?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? ctorMsg.ContextProperties.Add(new AOPProperty());?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??? }
在GetPropertiesForNewContext()方法中,添加了AOPProperty對象,它是一個上下文環境屬性:
??? public class AOPProperty : IContextProperty, IContributeObjectSink???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? public AOPProperty()?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? #region IContributeObjectSink Members?????????????????????????????? ????????????????????????
?????????????????????????
??????? public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return new AOPSink(nextSink);?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? #endregion?????????????????????????????? ????????????????????????
?????????????????????????
??????? #region IContextProperty Members?????????????????????????????? ????????????????????????
?????????????????????????
??????? public void Freeze(Context newContext)?????????????????????????????? ????????????????????????
??????? {??????????? ?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public bool IsNewContextOK(Context newCtx)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return true;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public string Name?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? get { return "AOP"; }?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? #endregion
AOPProperty屬性實現了接口IContextProperty,IContributeObjectSink。GetObjectSink()方法為IContributeObjectSink接口的方法,在其實現中,創建了一個IMessageSink對象AOPSink,該對象實現了IMessageSink接口:
??? public class AOPSink : 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;?????????????????????????????? ????????????????????????
if (call.MethodName == "AddOrder" || call.MethodName == "DeleteOrder")?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? if (permissions.Verify(Permission.ADMIN))?????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
????????? retMsg = m_NextSink.SyncProcessMessage(msg);?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?? ?????????????????????????????????????? ????????????????????????
??????????? return retMsg;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return null;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??? }
在AOPSink中,最重要的是SyncProcessMessage()方法,在這個方法中,實現了權限控制,并通過IMessage,截取了需要權限控制的方法。在檢驗了權限之后,然后再執行OrderManager的AddOrder和DeleteOrder方法。
?????????????????????????
通過AOP的實現,原來的OrderManager,就可以修改為:
[AOP]?????????????????????????????? ????????????????????????
public class OrderManager: ContextBoundObject
{
private ArrayList m_Orders;
public OrderManager()
{
?????? m_Orders = new ArrayList();
}
public void AddOrder(Order order)
{
??? m_Orders.Add(order);
}
public void RemoveOrder(Order order)
{
??? m_Orders.Remove(order);
}
}
在上述的OderManager類中,完全消除了permissions.Verify()等有關權限的代碼,解除了訂單管理與權限管理之間的耦合。
?????????????????????????
二、與AspectJ比較?????????????????????????? ????????????????????????
上述的方案雖然解除了訂單管理與權限管理的耦合,但從SyncProcessMessage()方法可以看出,它的實現具有很大的局限性。試想一下這樣的應用場景,在訂單管理系統中,用戶要求對修改訂單的方法增加權限驗證,同時要求在驗證權限時,允許業務經理(Permission.Manager)也具備管理訂單的權限,應該怎樣做?仔細思考,我們會發覺以上的實現未免太過死板了。
?????????????????????????
讓我們來參考一下AspectJ在java中的實現。AspectJ提供了自己的一套語法,其中包括aspect、pointcut、before、after等。我們可以通過aspect定義一個“方面”,如上的權限管理:
private static aspect AuthorizationAspect{……}
?????????????????????????
pointcut為切入點,在其中定義了需要截取上下文消息的方法,例如:
private pointcut authorizationExecution():
execution(public void OrderManager.AddOrder(Order)) ||
execution(public void OrderManager.DeleteOrder(Order)) ||
execution(public void OrderManager.UpdateOrder(Order));
?????????????????????????
由于權限驗證是在訂單管理方法執行之前完成,因此在before中,定義權限檢查:
before(): authorizationExecution()
{
if !(permissions.Verify(Permission.ADMIN))
{
??? throw new UnauthorizedException();
}
}
從上述AspectJ的實現中,我們可以看到,要定義自己的aspect是非常容易的,而通過pointcut的方式,可以將需要截取消息的方法,集中在一起。before和after則是具體的方面執行的邏輯,它們就好像Decorator模式那樣,對原有方法進行了一層裝飾,從而達到將aspect代碼植入的目的。
?????????????????????????
另外,AspectJ還提供了更簡單的語法,可以簡化前面pointcut中一系列方法的列舉:
private pointcut authorizationExecution():
execution (public * OrderManager.*(.))
?????????????????????????
AspectJ在應用AOP領域,已經非常成熟。它提供了自成一體的特有AspectJ語法,并需要專門的java編譯器,使用起來較為復雜。那么,在.Net下,可否實現類似AspectJ的功能呢?我想,由于.Net與java在很多技術的相似性,它們彼此之間在很多領域是相通的,因此要達到這一目標應該是可行的。事實上,開源項目中的Aspect#,就與AspectJ相似。
?????????????????????????
事實上,如果我們利用前面描述的動態代理機制,輔以設計模式的OO設計方法,直接在代碼中也可以實現AspectJ中的部分AOP特性。
?????????????????????????
三、.Net中AOP的深入實現?????????????????????????? ????????????????????????
我們先分析AspectJ中的pointcut和.Net中的SyncProcessMessage()方法。Pointcut可以添加一系列需要截取上下文的方法,那么在.Net中,我們也可以利用集合,動態地添加方法,并創建這些方法與“方面”的映射。同樣的,AspectJ中的before和after,是“方面”的核心實現,那么在.Net中,我們也可以利用委托,使其對應相關的方法,來實現其核心邏輯。
?????????????????????????
結合動態代理的知識,我們先定義兩個委托,分別代表before和after操作:
public delegate void BeforeAOPHandle(IMethodCallMessage callMsg);?????????????????????????????? ????????????????????????
public delegate void AfterAOPHandle(IMethodReturnMessage replyMsg);
?????????????????????????
BeforeAOPHandle中的參數callMsg,其值為要截取上下文的方法的消息;AfterAOPHandle中的參數replyMsg,則是該方法執行后返回的消息。
?????????????????????????
接下來,定義一個抽象基類AOPSink,它實現了IMessageSink接口:
public abstract class AOPSink : IMessageSink???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? private SortedList m_BeforeHandles;?????????????????????????????? ????????????????????????
??????? private SortedList m_AfterHandles;?????????????????????????????? ????????????????????????
??????? private IMessageSink m_NextSink;?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
在類AOPSink中,定義了兩個SortedList類型的字段:m_BeforeHandles和m_AfterHandles。它們負責存放方法名與BeforeAOPHandle和AfterAOPHandle對象之間的映射。添加這些映射的職責由如下兩個方法完成:?????????????????????????? ????????????????????????
protected virtual void AddBeforeAOPHandle(string methodName, BeforeAOPHandle beforeHandle)?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
???? lock (this.m_BeforeHandles)?????????????????????????????? ????????????????????????
???? {?????????????????????????????? ????????????????????????
???????? if (!m_BeforeHandles.Contains(methodName))?????????????????????????????? ????????????????????????
? ???????{?????????????????????????????? ????????????????????????
???????????? m_BeforeHandles.Add(methodName, beforeHandle);?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
???? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
protected virtual void AddAfterAOPHandle(string methodName, AfterAOPHandle afterHandle)?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
????? lock (this.m_AfterHandles)?????????????????????????????? ????????????????????????
????? {?????????????????????????????? ????????????????????????
????????? if (!m_AfterHandles.Contains(methodName))?????????????????????????????? ????????????????????????
????????? {?????????????????????????????? ????????????????????????
????????????? m_AfterHandles.Add(methodName, afterHandle);?????????????????????????????? ????????????????????????
????????? }?????????????????????????????? ????????????????????????
????? }?????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
考慮到我們要截取的方法可能會有多個,因此在類AOPSink中,又定義了兩個抽象方法,負責添加所有的映射關系:?????????????????????????? ????????????????????????
protected abstract void AddAllBeforeAOPHandles();?????????????????????????????? ????????????????????????
protected abstract void AddAllAfterAOPHandles();?????????????????????????? ????????????????????????
?????????????????????????
然后在構造函數中,我們初始化兩個SortedList對象,并調用上述的兩個抽象方法:?????????????????????????? ????????????????????????
??????? public AOPSink(IMessageSink nextSink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
? ??????????m_NextSink = nextSink; ????????????????????????
??????????? m_BeforeHandles = new SortedList();?????????????????????????????? ????????????????????????
??????????? m_AfterHandles = new SortedList();?????????????????????????????? ????????????????????????
??????????? AddAllBeforeAOPHandles();?????????????????????????????? ????????????????????????
??????????? AddAllAfterAOPHandles();?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
為了能夠根據方法名獲得相對應的委托對象,我們又定義了兩個Find方法。考慮到可能會有多個用戶同時調用,在這兩個方法中,我利用lock避免了對象的爭用:?????????????????????????? ????????????????????????
??????? protected BeforeAOPHandle FindBeforeAOPHandle(string methodName)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? BeforeAOPHandle beforeHandle;?????????????????????????????? ????????????????????????
??????????? lock (this.m_BeforeHandles)?????????????????????????????? ????????????????????????
??????????? {?????????????????????????????? ????????????????????????
??????????????? beforeHandle = (BeforeAOPHandle)m_BeforeHandles[methodName];?????????????????????????????? ????????????????????????
??????????? }?????????????????????????????? ????????????????????????
??????????? return beforeHandle;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??????? protected AfterAOPHandle FindAfterAOPHandle(string methodName)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? AfterAOPHandle afterHandle;?????????????????????????????? ????????????????????????
??????????? lock (this.m_AfterHandles)?????????????????????????????? ????????????????????????
???? ???????{?????????????????????????????? ????????????????????????
??????????????? afterHandle = (AfterAOPHandle)m_AfterHandles[methodName];?????????????????????????????? ????????????????????????
??????????? }?????????????????????????????? ????????????????????????
??????????? return afterHandle;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
接下來是IMessageSink接口要求實現的方法和屬性:?????????????????????????? ????????????????????????
??????? public IMessageSink NextSink?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? get { return m_NextSink; }?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public IMessage SyncProcessMessage(IMessage msg)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? IMethodCallMessage call = msg as IMethodCallMessage;?????????????????????????????? ????????????????????????
??????????? string methodName = call.MethodName.ToUpper();?????????????????????????????? ????????????????????????
??????????? BeforeAOPHandle beforeHandle = FindBeforeAOPHandle(methodName);?????????????????????????????? ????????????????????????
??????????? if (beforeHandle != null)?????????????????????????????? ????????????????????????
??????????? {?????????????????????????????? ????????????????????????
??????????????? beforeHandle(call);?????????????????????????????? ????????????????????????
??????????? }?????????????????????????????? ????????????????????????
??????????? IMessage retMsg = m_NextSink.SyncProcessMessage(msg);?????????????????????????????? ????????????????????????
??????????? IMethodReturnMessage replyMsg = retMsg as IMethodReturnMessage;?????????????????????????????? ????????????????????????
??????????? AfterAOPHandle afterHandle = FindAfterAOPHandle(methodName);?????????????????????????????? ????????????????????????
??????????? if (afterHandle != null)?????????????????????????????? ????????????????????????
??????????? {?????????????????????????????? ????????????????????????
????? ??????????afterHandle(replyMsg);?????????????????????????????? ????????????????????????
??????????? }?????????????????????????????? ????????????????????????
??????????? return retMsg;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return null;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
需要注意的是SyncProcessMessage()方法。在該方法中,通過FindBeforeAOPHandle()和FindAfterAOPHandle()方法,找到BeforeAOPHandle和AfterAOPHandle委托對象,并執行它們。即執行這兩個委托對象具體指向的方法,類似與AspectJ中的before和after的execution。?????????????????????????? ????????????????????????
?????????????????????????
現在,我們就可以象AspectJ那樣定義自己的aspect了。如權限管理一例,我們定義一個類AuthorizationAOPSink,它繼承了AOPSink:?????????????????????????? ????????????????????????
public class AuthorizationAOPSink : AOPSink???????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? public AuthorizationAOPSink(IMessageSink nextSink)?????????????????????????????? ????????????????????????
??????????? : base(nextSink)?????????????????????????????? ????????????????????????
?? {?????????????????????????????? ????????????????????????
?? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
然后在這個方法中,實現before和after的邏輯。注意before和after方法應與之前定義的委托BeforeAOPHandle和AfterAOPHandle一致。不過,以本例而言,并不需要實現after邏輯:?????????????????????????? ????????????????????????
private void Before_Authorization(IMethodCallMessage callMsg)?????????????????????????????? ????????????????????????
{?????? ?????????????????????????????? ????????????????????????
??????? if (callMsg == null)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
????????????? return;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??????? if (!permissions.Verify(Permission.ADMIN))?????????????????????????? ????????????????????????
?????? ?{?????????????????????????????? ????????????????????????
???????????????? throw UnauthorizedException();?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
}?????????????????????????? ????????????????????????
然后我們override基類中的抽象方法AddAllBeforeAOPHandles()和AddAllAfterAOPHandles():?????????????????????????? ????????????????????????
protected override void AddAllBeforeAOPHandles()?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? AddBeforeAOPHandle("ADDORDER", new BeforeAOPHandle(Before_Authorization));?????????????????????????????? ????????????????????????
??????????? AddBeforeAOPHandle("DELETEORDER", new BeforeAOPHandle(Before_Authorization));?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? protected override void AddAllAfterAOPHandles()?????????????????????????????? ????????????????????????
??????? {??????????? ?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
因為after邏輯不需要實現,因此重寫AddAllAfterAOPHandles()時,使其為空就可以了(必須重寫,因為該方法為抽象方法)。在AOPProperty類中,需要返回IMessageSink對象,所以還應修改原來的AOPProperty類中的GetObjectSink方法:?????????????????????????? ????????????????????????
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return new AOPSink(nextSink);???????????????????????????????? ????????????????????????
return new AuthorizationAOPSink(nextSink);??????? ?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
比較一下上述的實現方案,自定義的繼承AOPSink類的AuthorizationAOPSink就相當于AspectJ中的aspect。而與BeforeAOPHandle和AfterAOPHandle委托對應的方法,則相當于AspectJ的before和after語法。AddAllBeforeAOPHandles()和AddAllAfterAOPHandle()則相當于AspectJ的pointcut。通過引入委托的方法,使得我們的AOP實現,具有了AspectJ的一些特性,而這些實現是不需要專門的編譯器的。
?????????????????????????
很明顯,如果我們要求OrderManager類中新增的UpdateOrder方法,也要加入權限控制,那么我們可以在AddAllBeforeAOPHandles()方法中,增加UpdaeOrder方法與before邏輯的映射:
AddBeforeAOPHandle("UPDATEORDER", Before_Authorization);?????????????????????????????? ????????????????????????
同樣的,如果要對權限控制進行修改,開發業務經理對訂單管理的權限,那么也只需要修改Before_Authorization()方法:
private void Before_Authorization(IMessage callMsg)?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
?????? IMethodCallMessage call = callMsg as IMethodCallMessage;?????????????????????????????? ????????????????????????
??????? if (call == null)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
????????????? return;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??????? if (!(permissions.Verify(Permission.ADMIN)|| permissions.Verify(Permission.MANAGER)))?????????????????????????? ????????????????????????
?????? ?{?????????????????????????????? ????????????????????????
???????????????? throw UnauthorizedException();?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
?????????????????????????
四、進一步完善?????????????????????????? ????????????????????????
由于我們的委托列表m_BeforeHandles和m_AfterHandles為SortedList類型,因此作為key的methodName必須是唯一的。如果系統要求添加其他權限控制的邏輯,例如增加認證功能,就不能再在AuthorizationAOPSink類的AddAllBeforeAOPHandles()方法中增加方法名與認證功能的before邏輯之間的映射了。?????????????????????????? ????????????????????????
private void Before_Authentication(IMessage callMsg){……}?????????????????????????????? ????????????????????????
protected override void AddAllBeforeAOPHandles()?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
?????? ……?????????????????????????????? ????????????????????????
?????? AddBeforeAOPHandle("ADDORDER", new BeforeAOPHandle(Before_ Authentication));?????????????????????????????? ????????????????????????
?????? AddBeforeAOPHandle("DELETEORDER", new BeforeAOPHandle(Before_ Authentication));?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
如果在AuthorizationAOPSink類中添加上面的代碼,由于新增的“ADDORDER”key與前面重復,故執行程序時,是找不到相應的委托Before_Authentication的。?????????????????????????? ????????????????????????
?????????????????????????
解決的辦法就是為認證功能新定義一個aspect。由于在本方案中,實現AOP功能的不僅僅是實現了IMessageSink接口的AOPSink類,同時該類還與Property、Attribute有關。也就是說,如果我們新定義一個AuthenticationAOPSink,那么還要定義與之對應的AuthenticationAOPProperty類。為便于擴展,我采用了Template Method模式,為所有的property定義了抽象類AOPProperty,其中的抽象方法或虛方法,則留待其子類來實現。?????????????????????????? ????????????????????????
??? public abstract class AOPProperty : IContextProperty, IContributeObjectSink???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? protected abstract IMessageSink CreateSink(IMessageSink nextSink);?????????????????????????????? ????????????????????????
??????? protected virtual string GetName()?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return "AOP";?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? protected virtual void FreezeImpl(Context newContext)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??????? protected virtual bool CheckNewContext(Context newCtx)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return true;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? #region IContributeObjectSink Members?????????????????????????????? ????????????????????????
??????? public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return CreateSink(nextSink);?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??????? #endregion?????????????????????????????? ????????????????????????
?????????????????????????
??????? #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?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
與原來的AOPProperty類相比,IContextProperty,IContributeObjectSink接口的方法與屬性,都沒有直接實現,而是在其內部調用了相關的抽象方法和虛方法。包括:抽象方法CreateSink(),虛方法FreezeImpl(),CheckNewContext()以及GetName()。對于其子類而言,需要override的,主要是抽象方法CreateSink()和GetName()(因為Property的Name必須是唯一的),至于其他虛方法,可以根據需要選擇是否override。例如,自定義權限控制的屬性類AuthorizationAOPProperty:?????????????????????????? ????????????????????????
??? public class AuthorizationAOPProperty :AOPProperty???????????????????????????????? ????????????????????????
??? {??? ?????????????????????????????? ????????????????????????
??????? protected override IMessageSink CreateSink(IMessageSink nextSink)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return new AuthorizationAOPSink(nextSink);?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? protected override string GetName()?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return "AuthorizationAOP";?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????? ????????????????????????
在該類中,我們override了CreateSink()方法,創建了一個AuthorizationAOPSink對象。同時override了虛方法GetName,返回了自己的一個名字“AuthorizationAOP”。?????????????????????????? ????????????????????????
?????????????????????????
關于Attribute類,觀察其方法GetPropertiesForNewContext(),其實現是在IConstructionCallMessage消息的上下文property中添加自定義property。這些property組成了一個鏈,它是可以靜態添加的。鑒于此,我們可以采取兩種策略:?????????????????????????? ????????????????????????
1、 所有的aspect都使用同一個Attribute。其實現如下:?????????????????????????? ????????????????????????
??? [AttributeUsage(AttributeTargets.Class)]?????????????????????????????? ????????????????????????
??? public class AOPAttribute:ContextAttribute???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? public AOPAttribute()?????????????????????????????? ????????????????????????
??????????? : base("AOP")?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
? ??????????ctorMsg.ContextProperties.Add(new AuthorizationAOPProperty());?????????????????????????????? ????????????????????????
??????????? ctorMsg.ContextProperties.Add(new AuthenticationAOPProperty());?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
在方法GetPropertiesForNewContext()中,添加多個自定義Property。在添加Property時,需要注意添加Property的順序。?????????????????????????? ????????????????????????
2、 不同的aspect使用不同的Attribute。此時可以為這些Attribute定義一個共同的抽象基類AOPAttribute:?????????????????????????? ????????????????????????
[AttributeUsage(AttributeTargets.Class)]?????????????????????????????? ????????????????????????
??? public abstract class AOPAttribute:ContextAttribute???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? public AOPAttribute()?????????????????????????????? ????????????????????????
???????????? : base("AOP")?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? public sealed override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? ctorMsg.ContextProperties.Add(GetAOPProperty());?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected abstract AOPProperty GetAOPProperty();???????? ?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
注:我將GetPropertiesForNewContext()方法sealed,目的是不需要其子類在重寫該方法。?????????????????????????? ????????????????????????
?????????????????????????
繼承AOPAttribute類的子類只需要重寫GetAOPProperty()方法即可。但在為OrderManager類定義Attribute的時候,需注意其順序。如以下的順序:?????????????????????????? ????????????????????????
[AuthorizationAOP]?????????????????????????????? ????????????????????????
[AuthenticationAOP]?????????????????????????????? ????????????????????????
public class OrderManager{}?????????????????????????????? ????????????????????????
此時,AuthorizationAOPAttribute在前,AuthenticationAOPAttribute在后。如果以Decorator的角度來看,對被裝飾的方法,AuthorizationAOPAttribute在內,AuthenticationAOPAttribute在外。?????????????????????????? ????????????????????????
考慮到aspect的應用,有的方法需要多個aspect,有的則只需要單個aspect,所以,第二個方案更佳。?????????????????????????? ????????????????????????
?????????????????????????
五、AOP實例?????????????????????????? ????????????????????????
接下來,我通過一個實例,介紹AOP的具體實現。假定我們要設計一個計算器,它能提供加法和減法功能。我們希望,在計算過程中,能夠通過日志記錄整個計算過程及其結果,同時需要監測其運算性能。該例中,核心業務是加法和減法,而公共的業務則是日志與監測功能。根據前面對AOP的分析,這兩個功能應為我們整個系統需要剝離出來的“方面”。?????????????????????????? ????????????????????????
?????????????????????????
我們已經擁有了一個AOP實現機制,以及核心的類庫,包括AOPSink、AOPProperty、AOPAttribute三個抽象基類。現在,我們分別為日志aspect和監測aspect,定義相應的Sink、Property、Attribute。?????????????????????????? ????????????????????????
?????????????????????????
首先是日志aspect:?????????????????????????? ????????????????????????
LogAOPSink.cs:?????????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Messaging;?????????????????????????????? ????????????????????????
using Wayfarer.AOP;?????????????????????????????? ????????????????????????
?????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for LogAOPSink.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? public class LogAOPSink:AOPSink???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? public LogAOPSink(IMessageSink nextSink):base(nextSink)?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? ?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected override void AddAllBeforeAOPHandles()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
?????????? ???AddBeforeAOPHandle("ADD",new BeforeAOPHandle(Before_Log));?????????????????????????????? ????????????????????????
???????????? AddBeforeAOPHandle("SUBSTRACT",new BeforeAOPHandle(Before_Log));?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected override void AddAllAfterAOPHandles()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? AddAfterAOPHandle("ADD",new AfterAOPHandle(After_Log));?????????????????????????????? ????????????????????????
???????????? AddAfterAOPHandle("SUBSTRACT",new AfterAOPHandle(After_Log));?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? private void Before_Log(IMethodCallMessage callMsg)?????????????????????????????? ????????????????????????
???????? {??????????? ?????????????????????????????? ????????????????????????
???????????? if (callMsg == null)?????????????????????????????? ????????????????????????
???????????? {?????????????????????????????? ????????????????????????
????????????????? return;?????????????????????????????? ????????????????????????
???????????? }??????????? ?????????????????????????????? ????????????????????????
????????????????????? ?Console.WriteLine("{0}({1},{2})",callMsg.MethodName,callMsg.GetArg(0),callMsg.GetArg(1));?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? private void After_Log(IMethodReturnMessage replyMsg)?????????????????????????????? ????????????????????????
???????? {??????????? ?????????????????????????????? ????????????????????????
???????????? if (replyMsg == null)?????????????????????????????? ????????????????????????
???????????? {?????????????????????????????? ????????????????????????
????????????????? return;?????????????????????????????? ????????????????????????
???????????? }??????????? ?????????????????????????????? ????????????????????????
???????????? Console.WriteLine("Result is {0}",replyMsg.ReturnValue);????????? ?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
?????????????????????????
LogAOPProperty.cs?????????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
using Wayfarer.AOP;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Messaging;?????????????????????????????? ????????????????????????
?????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for LogAOPProperty.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? public class LogAOPProperty:AOPProperty???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? protected override IMessageSink CreateSink(IMessageSink nextSink)?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? return new LogAOPSink(nextSink);?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected override string GetName()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? return "LogAOP";?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
LogAOPAttribute.cs:?????????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Activation;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Contexts;?????????????????????????????? ????????????????????????
using Wayfarer.AOP;?????????????????????????????? ????????????????????????
?????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for LogAOPAttribute.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? [AttributeUsage(AttributeTargets.Class)]?????????????????????????????? ????????????????????????
??? public class LogAOPAttribute:AOPAttribute???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? protected override AOPProperty GetAOPProperty()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? return new LogAOPProperty();?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? ?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
?????????????????????????
然后再定義監測aspect:?????????????????????????? ????????????????????????
MonitorAOPSink.cs:?????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Messaging;?????????????????????????????? ????????????????????????
using Wayfarer.AOP;?????????????????????????????? ????????????????????????
?????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for MonitorAOPSink.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? public class MonitorAOPSink:AOPSink???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? public MonitorAOPSink(IMessageSink nextSink):base(nextSink)?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? ?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected override void AddAllBeforeAOPHandles()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? AddBeforeAOPHandle("ADD",new BeforeAOPHandle(Before_Monitor));?????????????????????????????? ????????????????????????
???????????? AddBeforeAOPHandle("SUBSTRACT",new BeforeAOPHandle(Before_Monitor));?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
???????? protected override void AddAllAfterAOPHandles()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? AddAfterAOPHandle("ADD",new AfterAOPHandle(After_Monitor));?????????????????????????????? ????????????????????????
???????????? AddAfterAOPHandle("SUBSTRACT",new AfterAOPHandle(After_Monitor));?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? private void Before_Monitor(IMethodCallMessage callMsg)?????????????????????????????? ????????????????????????
???????? {??????????? ?????????????????????????????? ????????????????????????
???????????? if (callMsg == null)?????????????????????????????? ????????????????????????
???????????? {?????????????????????????????? ????????????????????????
????????????????? return;?????????????????????????????? ????????????????????????
???????????? }?????????????????????????????? ????????????????????????
???????????? Console.WriteLine("Before {0} at {1}",callMsg.MethodName,DateTime.Now);?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
???????? private void After_Monitor(IMethodReturnMessage replyMsg)?????????????????????????????? ????????????????????????
???????? {??????????? ?????????????????????????????? ????????????????????????
???????????? if (replyMsg == null)?????????????????????????????? ????????????????????????
???????????? {?????????????????????????????? ????????????????????????
????????????????? return;?????????????????????????????? ????????????????????????
???????????? }?????????????????????????????? ????????????????????????
???????????? Console.WriteLine("After {0} at {1}",replyMsg.MethodName,DateTime.Now);?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
MonitorAOPProperty.cs:?????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
using Wayfarer.AOP;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Messaging;?????????????????????????????? ????????????????????????
?????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for MonitorAOPProperty.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? public class MonitorAOPProperty:AOPProperty???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? public MonitorAOPProperty()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? //???????????????????????????????? ????????????????????????
???????????? // TODO: Add constructor logic here???????????????????????????????? ????????????????????????
???????????? //???????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected override IMessageSink CreateSink(IMessageSink nextSink)?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? return new MonitorAOPSink(nextSink);?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
?????????????????????????
???????? protected override string GetName()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? return "MonitorAOP";?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
MonitorAOPAttribute.cs:?????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Activation;?????????????????????????????? ????????????????????????
using System.Runtime.Remoting.Contexts;?????????????????????????????? ????????????????????????
using Wayfarer.AOP;?????????????????????????????? ????????????????????????
?????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for MonitorAOPAttribute.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? [AttributeUsage(AttributeTargets.Class)]?????????????????????????????? ????????????????????????
??? public class MonitorAOPAttribute:AOPAttribute???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? protected override AOPProperty GetAOPProperty()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? return new MonitorAOPProperty();?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
注意在這兩個方面中,各自的Property的Name必須是唯一的。?????????????????????????? ????????????????????????
現在,可以定義計算器類。?????????????????????????? ????????????????????????
Calculator.cs:?????????????????????????? ????????????????????????
using System;
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for Calculator.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? [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;?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
需要注意的是Calculator類必須繼承ContextBoundObject類。?????????????????????????? ????????????????????????
最后,我們寫一個控制臺程序來執行Calculator:?????????????????????????? ????????????????????????
Program.cs:?????????????????????????????? ????????????????????????
using System;?????????????????????????????? ????????????????????????
namespace Wayfarer.AOPSample?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? /// <summary>???????????????????????????????? ????????????????????????
??? /// Summary description for Class1.???????????????????????????????? ????????????????????????
??? /// </summary>???????????????????????????????? ????????????????????????
??? class Program???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
???????? /// <summary>???????????????????????????????? ????????????????????????
???????? /// The main entry point for the application.???????????????????????????????? ????????????????????????
???????? /// </summary>???????????????????????????????? ????????????????????????
???????? [STAThread]?????????????????????????????? ????????????????????????
???????? static void ?????????????????????????? Main?????????????????????????? (string[] args)???????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? Calculator cal = new Calculator();?????????????????????????????? ????????????????????????
???????????? cal.Add(3,5);?????????????????????????????? ????????????????????????
???????????? cal.Substract(3,5);?????????????????????????????? ????????????????????????
???????????? Console.ReadLine();?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
運行結果如下:?????????????????????????? ????????????????????????
?
六、結論?????????????????????????? ????????????????????????
在.Net平臺下采用動態代理技術實現AOP,其原理并不復雜,而.Net Framework也提供了足夠的技術來實現它。如果再結合好的設計模式,提供一個基本的AOP框架,將大大地簡化開發人員處理“aspect”的工作。當然,本文雖然提供了實現AOP的實例,但其架構的設計還遠遠不能達到企業級的要求,如在穩定性、可擴展性上還需經過進一步的測試與改善。例如我們可以通過配置文件的形式,來配置方法與方面之間的映射。同時,由于采用了動態代理,在性能上還期待改進。?????????????????????????? ????????????????????????
?????????????????????????
使用動態代理技術實現AOP,對實現AOP的類有一個限制,就是必須派生于ContextBoundObject類,這對于單繼承語言來說,確實是一個比較致命的缺陷。所謂“仁者見仁,智者見智”,這就需要根據項目的情況,做出正確的抉擇了。?????????????????????????? ????????????????????????
?????????????????????????
參考:?????????????????????????? ????????????????????????
1、 JGTM,《A Taste of AOP from Solving Problems with OOP and Design Patterns》?????????????????????????? ????????????????????????
2、 NiWalker,《Attribute在.Net編程的應用》?????????????????????????? ????????????????????????
3、板橋里人,《AOP與權限控制實現》????????????????????????
在《在.Net中關于AOP的實現》我通過動態代理的技術,基本上實現了AOP的幾個技術要素,包括aspect,advice,pointcut。在文末我提到采用配置文件方式,來獲取advice和pointcut之間的映射,從而使得構建aspect具有擴展性。
細細思考這個問題,我發現使用delegate來構建advice,似乎并非一個明智的選擇。我在建立映射關系時,是將要攔截的方法名和攔截需要實現的aspect邏輯建立一個對應關系,而該aspect邏輯確實可以通過delegate,使其指向一族方法簽名與該委托完全匹配的方法。這使得advice能夠抽象化,以便于具體實現的擴展。然而,委托其實現畢竟是面向過程的范疇,雖然在.Net下,delegate本身仍是一個類對象,然而在創建具體的委托實例時,仍然很難通過配置文件和反射技術來獲得。
?????????????????????????
考慮到委托具有的接口抽象的本質,也許采用接口的方式來取代委托更為可行。在之前的實現方案中,我為advice定義了兩個委托:
public delegate void BeforeAOPHandle(IMethodCallMessage callMsg);?????????????????????????????? ????????????????????????
public delegate void AfterAOPHandle(IMethodReturnMessage replyMsg);?????????????????????????????? ????????????????????????
?????????????????????????
我可以定義兩個接口IBeforeAction和IAfterAction,分別與這兩個委托相對應:???????????????????????????????? ????????????????????????
??? public interface IBeforeAdvice???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? void BeforeAdvice(IMethodCallMessage callMsg);?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
??? public interface IAfterAdvice???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? void AfterAdvice(IMethodReturnMessage returnMsg);?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
通過定義的接口,可以將Advice與Aspect分離開來,這也完全符合OO思想中的“責任分離”原則。?????????????????????????? ????????????????????????
(注:為什么要為Advice定義兩個接口?這是考慮到有些Aspect只需要提供Before或After兩個邏輯之一,如權限控制,就只需要before Action。)?????????????????????????? ????????????????????????
?????????????????????????
那么當類庫使用者,要定義自己的Aspect時,就可以定義具體的Advice類,來實現這兩個接口,以及具體的Advice邏輯了。例如,之前提到的日志Aspect:?????????????????????????? ????????????????????????
??? 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?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
?????????????????????????
而在AOPSink類的派生類中,添加方法名與Advice映射關系(此映射關系,我們即可理解為AOP的pointcut)時,就可以添加實現了Advice接口的類對象,如:?????????????????????????? ????????????????????????
???????? public override void AddAllBeforeAdvices()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
??????????? AddBeforeAdvice("ADD",new LogAdvice());?????????????????????????????? ????????????????????????
??????????? AddBeforeAdvice("SUBSTRACT", new LogAdvice());?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
???????? public override void AddAllAfterAdvices()?????????????????????????????? ????????????????????????
???????? {?????????????????????????????? ????????????????????????
???????????? AddAfterAdvice("ADD",new LogAdvice());?????????????????????????????? ????????????????????????
??????????? AddAfterAdvice("SUBSTRACT", new LogAdvice());?????????????????????????????? ????????????????????????
???????? }?????????????????????????????? ????????????????????????
由于LogAdvice類實現了接口IBeforeAdvice和IAfterAdvice,因此諸如new LogAdvice的操作均可以通過反射來創建該實例,如:?????????????????????????? ????????????????????????
IBeforeAdvice beforeAdvice =?????????????????????????? ????????????????????????
(IBeforeAdvice)Activator.CreateInstance("Wayfarer.AOPSample","Wayfarer.AOPSample.LogAdvice").Unwrap();?????????????????????????????? ????????????????????????
而CreateInstance()方法的參數值,是完全可以通過配置文件來配置的:?????????????????????????? ????????????????????????
<aop>????????????????????????????? ????????????????????????
??? <aspect value ="LOG">??????????????????????????????? ????????????????????????
???????? <advice type="before" assembly="Wayfarer.AOPSample" class="Wayfarer.AOPSample.LogAdvice">??????????????????????????????? ????????????????????????
???????????? <pointcut>ADD</pointcut>??????????????????????????????? ????????????????????????
???????????? <pointcut>SUBSTRACT</pointcut>??????????????????????????????? ????????????????????????
???????? </advice>????????????????????????????? ????????????????????????
???????? <advice type="after" assembly="Wayfarer.AOPSample" class="Wayfarer.AOPSample.LogAdvice">??????????????????????????????? ????????????????????????
???????????? <pointcut>ADD</pointcut>??????????????????????????????? ????????????????????????
???????????? <pointcut>SUBSTRACT</pointcut>??????????????????????????????? ????????????????????????
???????? </advice>????????????????????????????? ????????????????????????
??? </aspect>??? ?????????????????????????????? ????????????????????????
</aop>?????????????????????????? ????????????????????????
這無疑改善了AOP實現的擴展性。?????????????????????????? ????????????????????????
?????????????????????????
《在.Net中關于AOP的實現》實現AOP的方案,要求包含被攔截方法的類必須繼承ContextBoundObject。這是一個比較大的限制。不僅如此,ContextBoundObject對程序的性能也有極大的影響。我們可以做一個小測試。定義兩個類,其中一個類繼承ContextBoundObject。它們都實現了一個累加的操作:
class NormalObject???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? public void Sum(int n)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
?????? ?????int sum = 0;?????????????????????????????? ????????????????????????
??????????? for (int i = 1; i <= n; i++)?????????????????????????????? ????????????????????????
??????????? {?????????????????????????????? ????????????????????????
??????????????? sum += i;?????????????????????????????? ????????????????????????
??????????? }?????????????????????????????? ????????????????????????
??????????? Console.WriteLine("The result is {0}",sum);?????????????????????????????? ????????????????????????
??????????? Thread.Sleep(10);?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
?????????????????????????
??? class MarshalObject:ContextBoundObject???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? public void Sum(int n)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? int sum = 0;?????????????????????????????? ????????????????????????
??????????? for (int i = 1; i <= n; i++)?????????????????????????????? ????????????????????????
??????????? {?????????????????????????????? ????????????????????????
??????????????? sum += i;?????????????????????????????? ????????????????????????
??????????? }?????????????????????????????? ????????????????????????
??????????? Console.WriteLine("The result is {0}", sum);?????????????????????????????? ????????????????????????
??????????? Thread.Sleep(10);?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??? }?????????????????????????????? ????????????????????????
然后執行這兩個類的Sum()方法,測試其性能:
??? class Program???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? static void ?????????????????????????? Main?????????????????????????? (string[] args)???????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? long normalObjMs, marshalObjMs;?????????????????????????????? ????????????????????????
??????????? Stopwatch watch = new Stopwatch();?????????????????????????????? ????????????????????????
??????????? NormalObject no = new NormalObject();?????????????????????????????? ????????????????????????
??????????? MarshalObject mo = new MarshalObject();?????????????????????????????? ????????????????????????
?????????????????????????
??????????? watch.Start();?????????????????????????????? ????????????????????????
??????????? no.Sum(1000000);?????????????????????????????? ????????????????????????
??????????? watch.Stop();?????????????????????????????? ????????????????????????
??????????? normalObjMs = watch.ElapsedMilliseconds;?????????????????????????????? ????????????????????????
??????????? watch.Reset();?????????????????????????????? ????????????????????????
?????????????????????????
??????????? watch.Start();?????????????????????????????? ????????????????????????
??????????? mo.Sum(1000000);?????????????????????????????? ????????????????????????
??????????? watch.Stop();?????????????????????????????? ????????????????????????
??????????? marshalObjMs = watch.ElapsedMilliseconds;?????????????????????????????? ????????????????????????
??????????? watch.Reset();?????????????????????????????? ????????????????????????
?????????????????????????
??????????? Console.WriteLine("The normal object consume {0} milliseconds.",normalObjMs);?????????????????????????????? ????????????????????????
??????????? Console.WriteLine("The contextbound object consume {0} milliseconds.",marshalObjMs);??????????? ?????????????????????????????? ????????????????????????
??????????? Console.ReadLine();?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??? }
得到的結果如下:
?
從性能的差異看,兩者之間的差距是比較大的。如果將其應用在企業級的復雜邏輯上,這種區別就非常明顯了,對系統帶來的影響也是非常巨大的。
?????????????????????????
另外,在《在.Net中關于AOP的實現》文章后,有朋友發表了很多中肯的意見。其中有人提到了AOPAttribute繼承ContextAttribute的問題。評論中提及微軟在以后的版本中,不再提供ContextAttribute。如果真是如此,確有必要放棄繼承ContextAttribute的形式。不過,在.Net中,除了ContextAttribute之外,還提供有一個接口IContextAttribute,該接口的定義為:
public interface IContextAttribute?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??????? void GetPropertiesForNewContext(IConstructionCallMessage msg);?????????????????????????????? ????????????????????????
??????? bool IsContextOK(Context ctx, IConstructionCallMessage msg);???????
}?????????????????????????????? ????????????????????????
此時只需要將原來的AOPAttribute實現該接口即可:???????????????????????????????? ????????????????????????
??? public abstract class AOPAttribute:Attribute,IContextAttribute//ContextAttribute???????????????????????????????? ????????????????????????
??? {?????????????????????????????? ????????????????????????
??????? #region IContextAttribute Members?????????????????????????????? ????????????????????????
??????? public void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? AOPProperty property = GetAOPProperty();?????????????????????????????? ????????????????????????
??????????? property.AspectXml = m_AspectXml;?????????????????????????????? ????????????????????????
??????????? property.AspectXmlFlag = m_AspectXmlFlag;?????????????????????????????? ????????????????????????
??????????? ctorMsg.ContextProperties.Add(property);?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
?????????????????????????
??????? public bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)?????????????????????????????? ????????????????????????
??????? {?????????????????????????????? ????????????????????????
??????????? return false;?????????????????????????????? ????????????????????????
??????? }?????????????????????????????? ????????????????????????
??????? #endregion?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
不知道,IContextAttribute似乎也會在未來的版本中被取消呢??????????????????????????? ????????????????????????
?????????????????????????
然而,從總體來看,這種使用ContextBoundObject的方式是不太理想的,也許它只能停留在實驗室階段,或許期待微軟在未來的版本中得到更好的解決!??????????????????????????? ????????????????????????
?????????????????????????
當然,如果采用Castle的DynamicProxy技術,可以突破必須繼承CotextBoundObject的局限,但隨著而來的局限卻是AOP攔截的方法,要求必須是virtual的。坦白說,這樣的限制,不過與前者乃“五十步笑百步”的區別而已。我還是期待有更好的解決方案。?????????????????????????? ????????????????????????
?????????????????????????
說到AOP的幾大要素,在這里可以補充說說,它主要包括:?????????????????????????? ????????????????????????
1、Cross-cutting concern
?????????????????????????
在OO模型中,雖然大部份的類只有單一的、特定的功能,但它們通常會與其他類有著共同的第二需求。例如,當線程進入或離開某個方法時,我們可能既要在數據訪問層的類中記錄日志,又要在UI層的類中記錄日志。雖然每個類的基本功能極然不同,但用來滿足第二需求的代碼卻基本相同。
?????????????????????????
2、Advice
?????????????????????????
它是指想要應用到現有模型的附加代碼。例如在《在.Net中關于AOP的實現》的例子中,是指關于打印日志的邏輯代碼。
?????????????????????????
3、Point-cut
?????????????????????????
這個術語是指應用程序中的一個執行點,在這個執行點上需要采用前面的cross-cutting concern。如例子中,執行Add()方法時出現一個Point-cut,當方法執行完畢,離開方法時又出現另一個Point-cut。
?????????????????????????
4、Aspect
?????????????????????????
Point-cut和advice結合在一起就叫做aspect。如例子中的Log和Monitor。在對本例的重構中,我已經AOPSink更名為Aspect,相應的LogAOPSink、MonitorAOPSink也更名為LogAspect,MonitorAspect。
?????????????????????????
以上提到的PointCut和Advice在AOP技術中,通常稱為動態橫切技術。與之相對應的,是較少被提及的靜態橫切。它與動態橫切的區別在于它并不修改一個給定對象的執行行為,相反,它允許通過引入附加的方法屬性和字段來修改對象固有的結構。在很多AOP實現中,將靜態橫切稱為introduce或者mixin。?????????????????????????? ????????????????????????
?????????????????????????
在開發應用系統時,如果需要在不修改原有代碼的前提下,引入第三方產品和API庫,靜態橫切技術是有很大的用武之地的。從這一點來看,它有點類似于設計模式中提到的Adapter模式需要達到的目標。不過,看起來靜態橫切技術應比Adapter模式更加靈活和功能強大。?????????????????????????? ????????????????????????
?????????????????????????
例如,一個已經實現了收發郵件的類Mail。然而它并沒有實現地址驗證的功能。現在第三方提供了驗證功能的接口IValidatable:?????????????????????????? ????????????????????????
public interface IValidatable?????????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
??? bool ValidateAddress();?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
如果沒有AOP,采用設計模式的方式,在不改變Mail類的前提下,可以通過Adapter模式,引入MailAdater,繼承Mail類,同時實現IValidatable接口。采用introduce技術,卻更容易實現該功能的擴展,我們只需要定義aspect:(注:java代碼,使用了AspectJ)?????????????????????????? ????????????????????????
import com.acme.validate.Validatable;?????????????????????????????? ????????????????????????
?????????????????????????
public aspect EmailValidateAspect?????????????????????????? ????????????????????????
{?????????????????????????????? ????????????????????????
?? declare parents: Email implements IValidatable;?????????????????????????????? ????????????????????????
?????????????????????????
?? public boolean Email.validateAddress(){?????????????????????????????? ????????????????????????
???? if(this.getToAddress() != null){?????????????????????????????? ????????????????????????
?????? ?? return true;?????????????????????????????? ????????????????????????
???? }else{?????????????????????????????? ????????????????????????
?????? ?? return false;?????????????????????????????? ????????????????????????
???? }?????????????????????????????? ????????????????????????
?? }?????????????????????????????? ????????????????????????
}?????????????????????????????? ????????????????????????
?????????????????????????
從上可以看到,通過EmailValidateAspect方面,為Email類introduce了新的方法ValidateAddress()。非常容易的就完成了Email的擴展。?????????????????????????? ????????????????????????
?????????????????????????
我們可以比較一下,如果采用Adapter模式,原有的Email類是不能被顯示轉換為IValidatable接口的,也即是說如下的代碼是不可行的:?????????????????????????? ????????????????????????
Email mail = new Email();?????????????????????????????? ????????????????????????
IValidatable validate = ((IValidatable)mail).ValidateAddress();?????????????????????????????? ????????????????????????
要調用ValidateAddress()方法,必須通過EmailAdapter類。然而通過靜態橫切技術,上面的代碼就完全可行了。?????????????????????????? ????????????????????????
?????????????????????????
靜態橫切的技術在企業應用上還需要進一步驗證和測試,不過遺憾的是,《在.Net中關于AOP的實現》一文采用的動態代理技術,是無法完成實現靜態橫切的目標的。
轉載于:https://www.cnblogs.com/wangwangfei/p/4387643.html
總結
以上是生活随笔為你收集整理的【转】在.Net中关于AOP的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mustache,用{{}}获取值
- 下一篇: .Net面试经验,从北京到杭州