【我们一起写框架】C#的AOP框架
前言
AOP,大家都是聽過的,它是一種面向切面的設(shè)計模式。
不過AOP雖然是被稱為設(shè)計模式,但我們應(yīng)該很少能看到AOP設(shè)計的框架。為什么呢?
因為,AOP單獨設(shè)計的框架幾乎是無法使用的。普遍的情況是,AOP要是和其他設(shè)計模式結(jié)合在一起使用。
所以,AOP雖然是設(shè)計模式,但我認為它更接近一種設(shè)計元素,是我們在設(shè)計框架的作料。
其實AOP的原理就是將公共的部分提取出來,這件事,即便不考慮設(shè)計模式,每個開發(fā)人員在工作時也是會做的。也就是說,在AOP設(shè)計模式被提出來之前,我們就在應(yīng)用AOP的設(shè)計了。
那么,為什么還要單獨將AOP拿出來說事呢?
我認為,主要目的應(yīng)該是要強化切面的重要性。因為設(shè)計框架時加入AOP的理念,確實會讓框架更加立體。
AOP的應(yīng)用
AOP既然是一種作料,那么它的應(yīng)用就是多種多樣的;它可以出現(xiàn)在任何場合的。
下面我們舉出一個例子,來說明AOP的應(yīng)用。
----------------------------------------------------------------------------------------------------
我們在開發(fā)的時候,通常會有這樣的需求。
[將函數(shù)的入?yún)⒑头祷刂涤涗浀饺罩局衇[入?yún)⒅袨樨摂?shù)拋出異常]
當我們面對這樣的需求時,通常會將入?yún)⒑头祷刂等總鞯揭粋€獨立的操作函數(shù)中,對其進行相應(yīng)的操作。
這樣實現(xiàn),就是AOP的理念;不過開發(fā)者處理時,稍微繁瑣了一點,因為每個函數(shù)都要處理。
為了減少這種重復(fù)操作,讓我們一起來編寫函數(shù)的切面AOP吧。
AOP框架的實現(xiàn)
首先,我們一起看下AOP框架應(yīng)用后的效果。
在下面代碼中,可以看到,我們定義了一個AOPTest類,然后調(diào)用了他的Test方法,之后傳入了一個正數(shù)和一個負數(shù),如果函數(shù)拋出異常,我們將輸出異常的消息。
class Program {static void Main(string[] args){AOPTest test = new AOPTest();try{ test.Test(518);test.Test(-100);}catch(Exception ex){Console.WriteLine(ex.Message);}Console.ReadLine();} }接下來我們看下AOPTest類的定義。
[Kiba] public class AOPTest : ContextBoundObject { public string Test(int para){Console.WriteLine(para);return "數(shù)字為:" + para;} }代碼如上所示,很簡單,就是輸出了入?yún)?#xff0c;不過有兩個地方需要注意,該類繼承了ContextBoundObject類,并且擁有一個KIba的特性。
然后,我們看下運行結(jié)果。
從運行結(jié)果中我們看到,第一個函數(shù)正常輸出,但第二個函數(shù)拋出了異常,而且異常的Message是異常兩個漢字。
這就是我們AOP實行的效果了,我們的AOP框架對函數(shù)入?yún)⑦M行了判斷,如果是正數(shù),就正常運行,如果為負數(shù)就拋出異常。
下面我們一起來看看AOP框架是如何實現(xiàn)這樣的效果的。
首先我們一起來看下Kiba這個特性。
[AttributeUsage(AttributeTargets.Class)] public class KibaAttribute : ContextAttribute {public KibaAttribute(): base("Kiba"){} public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg){ctorMsg.ContextProperties.Add(new KibaContextProperty()); } }代碼如上所示,很簡單很基礎(chǔ)的一個特性,不過它繼承了ContextAttribute類,并重寫了其下的方法GetPropertiesForNewContext。
這個方法是干什么的呢?
我們可以從函數(shù)名的直譯來理解它是干什么的,GetPropertiesForNewContext直譯過來就是創(chuàng)建新對象時獲取他的屬性。然后我們看到,我們重新了該方法后又為他添加了一個新的屬性。
而我們添加的這個新的屬性將截獲擁有該特性的類的函數(shù)。
【PS:該描述并不是ContextAttribute真實的運行邏輯,不過,初學時,我們可以先這樣理解,當我們更深入的理解了函數(shù)的運行機制后,自然就明白該類的意義。】
?下面我們看下KibaContextProperty類。
public class KibaContextProperty : IContextProperty, IContributeObjectSink {public KibaContextProperty(){} public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next){return new KibaMessageSink(next);} public bool IsNewContextOK(Context newCtx){return true;} public void Freeze(Context newCtx){} public string Name{get { return "Kiba"; }} }代碼如上所示,依然很簡單,只是繼承并實現(xiàn)了IContextProperty和IContributeObjectSink兩個接口。
其中我們重點看下GetObjectSink方法,該方法用于截獲函數(shù)。
我們可以看到該方法的兩個參數(shù),但我們只用到了一個IMessageSink ,并且,該方法的返回值也是IMessageSink。
所以,我們可以想到,該方法的本來面目是這樣的。
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next) {return next; }也就是說,IMessageSink?封裝了函數(shù)的一切內(nèi)容,那么我們的AOP實現(xiàn)的地方也就找到了。
于是我們用KibaMessageSink類處理一下IMessageSink 。
KibaMessageSink代碼如下:
public class KibaMessageSink : IMessageSink {private KAspec kaspec = new KAspec(); private IMessageSink nextSink; public KibaMessageSink(IMessageSink next){nextSink = next;} public IMessageSink NextSink{get{return nextSink;}} public IMessage SyncProcessMessage(IMessage msg){ IMethodCallMessage call = msg as IMethodCallMessage; if (call != null){//攔截消息,做前處理kaspec.PreExcute(call.MethodName, call.InArgs);}for (int i = 0; i < call.InArgs.Count(); i++){var para = call.InArgs[i];var type = para.GetType();string typename = type.ToString().Replace("System.Nullable`1[", "").Replace("]", "").Replace("System.", "").ToLower();if (typename == "int32"){int inparame = Convert.ToInt16(call.InArgs[i]);if (inparame < 0){throw new Exception("異常");}} }//傳遞消息給下一個接收器 IMessage retMsg = nextSink.SyncProcessMessage(call as IMessage); IMethodReturnMessage dispose = retMsg as IMethodReturnMessage;if (dispose != null){ //調(diào)用返回時進行攔截,并進行后處理kaspec.EndExcute(dispose.MethodName, dispose.OutArgs, dispose.ReturnValue, dispose.Exception);} return retMsg;} public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink){return null;} }我們重點看下SyncProcessMessage方法。
可以看到,我們在方法調(diào)用先調(diào)用了KAspec類的PreExcute方法,該方法用于把入?yún)⑤敵龅饺罩局小?/p>
接下來,我們對入?yún)⑦M行了判斷,如果入?yún)⑹秦摂?shù),我們將不執(zhí)行函數(shù),直接拋出異常。
然后我們調(diào)用KAspec類的EndExcute方法,將返回值輸出到日志中。
再然后,我們才返回IMessage,讓函數(shù)完結(jié)。
下面我們一起看下KAspec類的實現(xiàn)。
/// <summary> /// 切面 /// </summary> public class KAspec { #region 處理/// <summary>/// 前處理/// </summary> public void PreExcute(string MethodName, object[] InParams){Logger.Info("==================== " + MethodName + ":" + " Start====================");Logger.Info(string.Format("參數(shù)數(shù)量:{0}", InParams.Count()));for (int i = 0; i < InParams.Count(); i++){Logger.Info(string.Format("參數(shù)序號[{0}] ============ 參數(shù)類型:{1} 執(zhí)行類:{1}", i + 1, InParams[i])); Logger.Info("傳入?yún)?shù):");string paramXMLstr = XMLSerializerToString(InParams[i], Encoding.UTF8);Logger.Info(paramXMLstr);}} /// <summary>/// 后處理/// </summary> public void EndExcute(string MethodName, object[] OutParams, object ReturnValue, Exception ex){Type myType = ReturnValue.GetType();Logger.Info(string.Format("返回值類型:{0}", myType.Name));Logger.Info("返回值:");if (myType.Name != "Void"){string resXMLstr = DataContractSerializerToString(ReturnValue, Encoding.UTF8);Logger.Info(resXMLstr);}if (OutParams.Count() > 0)//out 返回參數(shù){Logger.Info(string.Format("out返回參數(shù)數(shù)量:{0}", OutParams.Count())); for (int i = 0; i < OutParams.Count(); i++){Logger.Info(string.Format("參數(shù)序號[{0}] == 參數(shù)值:{1}", i + 1, OutParams[i]));}}if (ex != null){Logger.Error(ex);}Logger.Info("==================== " + MethodName + ":" + " End====================");} }代碼如上所示,就是簡單的日志輸出。
到此,我們的AOP框架就編寫完成了;其上的代碼編寫都是為KAspec服務(wù),因為KAspec才是切面。
也就是說,只要將特性Kiba賦予給類,那么該類的函數(shù),就被攔截監(jiān)聽,然后我們就可以KAspec切面中,做我們想做的操作了。
最后,我們再回頭看下AOPTest類。
[Kiba] public class AOPTest : ContextBoundObject可以看到,該類不止擁有Kiba特性,還繼承了ContextBoundObject類,該類是干什么的呢?
ContextBoundObject類是內(nèi)容邊界對象,只有繼承了ContextBoundObject類的類,其類中才會駐留的Context上下文,并且會被ContextAttribute特性攔截監(jiān)聽。
呃,其實,這樣解釋還是有點不太正確,不過我也沒找到更好的說明方式,如果你還理解不了,也可以去MSDN查詢下,當然,MSDN的解釋是反人類的,需要做好心理準備。
----------------------------------------------------------------------------------------------------
框架代碼已經(jīng)傳到Github上了,歡迎大家下載。
Github地址:https://github.com/kiba518/KAOP
----------------------------------------------------------------------------------------------------
注:此文章為原創(chuàng),歡迎轉(zhuǎn)載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯,請點擊下右下角的【推薦】,非常感謝!
?
總結(jié)
以上是生活随笔為你收集整理的【我们一起写框架】C#的AOP框架的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Animate与transform的使用
- 下一篇: 阻止中文输入法输入拼音的时候触发inpu