PostSharp是一種Aspect Oriented Programming 面向切面(或面向方面)的元件框架,適用在.NET開發中,本篇主要介紹Postsharp在.NET開發中的相關知識,以及一些如日誌、快取、交易處理、異常處理等常用的切面處理操作。
AOP(Aspect-Oriented Programming)是一種將函數的輔助性功能與業務邏輯相分離的程式設計泛型(programming paradigm),其目的是將橫切關注點(cross-cutting concerns)分離出來,使得程式具有更高的模組化特性。 AOP是面向方面軟體開發(Aspect-Oriented Software Development)在編碼實現層面的具體表現。
我們知道,解耦是程式設計師編碼開發過程中一直追求的,AOP也是為了解耦所誕生。引入AOP技術,能很大程度簡化我們編碼,減少複製的程式碼量,也便於統一維護統一的部分程式碼,如日誌、快取、事務處理、異常處理等常用的處理。
1)AOP技術介紹
AOP技術利用一種稱為「橫切」的技術,剖解開封裝的物件內部,並將那些影響了多個類別的公共行為封裝到一個可重複使用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻為業務模組所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。 AOP代表的是一個橫向的關係,如果說「物件」是一個空心的圓柱體,其中封裝的是物件的屬性和行為;那麼面向方面程式設計的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息。而剖開的切面,也就是所謂的「面向」了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。
使用「橫切」技術,AOP把軟體系統分成兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處都基本相似。例如權限認證、日誌、事務處理。 Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分開。正如Avanade公司的高級方案構架師Adam Magee所說,AOP的核心思想就是“將應用程序中的商業邏輯同對其提供支持的通用服務進行分離。”
2)AOP使用場景
#AOP用來封裝橫切關注點,具體可以在下面的場景中使用:
Authentication 權限
Caching 快取
Context passing 內容傳遞
Lazy loading 懶載入
Debugging 調試
logging, tracing, profiling and monitoring 記錄追蹤最佳化校準
##Perfor##Performance optimization 效能最佳化
Persistence 持久化
Resource pooling 資源池
#Syn##chr## pooling 資源池
#Syn##chractions 交易
3)PostSharp框架####PostSharp是一個用於在.NET平台上實現AOP的框架,是比較常用的一個AOP框架,官方網站為//m.sbmmt.com/。目前最新版本為4.X,但是是收費的AOP軟體。 ###
PostSharp使用靜態織入方式實現AOP,其連接點非常豐富,使用簡單,而且相對其它一些.NET平台上的AOP框架來說,PostSharp較為輕量級,但是功能卻一點也不遜色。
整體來說,使用PostSharp,將會帶來以下優點:
#橫切關注點單獨分離出來,提高了程式碼的清晰性和可維護性。
只要在Aspect中寫輔助功能程式碼,在一定程度上減少了工作量和冗餘程式碼。
當然,使用PostSharp也會存在一些缺點,主要缺點有如下兩方面:
增加了除錯的難度。
比起不用AOP的程式碼,運作效率有所降低。
不過瑕不掩瑜,相對於這些缺點問題,使用PostSharp可以大幅提高開發效率,減少重複程式碼,從而提高程式碼的可讀性、可維護性。
另外在GitHub上還有一些開源的AOP元件,例如排頭位的是KingAOP(//m.sbmmt.com/),不過由於它採用了Dynamic的方式來實現,如它的構造物件如下所示。
dynamic helloWorld = new HelloWorld(); helloWorld.HelloWorldCall();
因此雖然比較方便,而且號稱和PostSharp使用習慣類似,但是改變了物件的建立方式,對一般專案的類別物件處理並不太適合。因此我還是比較傾向使用PostSharp來進行AOP的程式開發。
1)準備PostSharp的編譯環境
PostSharp目前版本是4.x ,我在官網下載了進行使用,不過經常發生"Error connecting to the pipe server. See previous warnings for details.",後來乾脆使用了3.x版本的,反而能夠正常使用,非常不錯,呵呵。
PostSharp是一個可以安裝在VS上的插件,安裝後在VS的選單欄位裡面增加了一個PostSharp的選單項,如下圖所示。
一般專案如果需要使用PostSharp特性的,在專案屬性的【PostSharp】選項頁中,使用【Add PostSharp to this project】把PostSharp加入到專案裡面進行使用。
新增後,會跳出PostSharp的外掛程式提示對話框,提示將加入對應的PostSharp套件等內容,如下所示。
完成後就可以在專案中使用PostSharp的相關類別了。
2)增加PostSharp的AOP切面處理
一般約定每個Aspect類別的命名必須為「XXXAttribute」的形式。其中「XXX」就是這個Aspect的名字。 PostSharp中提供了豐富的內建“Base Aspect”以便我們繼承,其中這裡我們繼承“OnMethodBoundaryAspect ”,這個Aspect提供了進入、退出函數等連接點方法。另外,Aspect上必須設定“[Serializable] ”,這與PostSharp內部對Aspect的生命週期管理有關。
日誌的Aspect類別的程式碼如下所示。
[Serializable] public class LogAttribute : OnMethodBoundaryAspect { public override void OnEntry(MethodExecutionArgs args) { Console.WriteLine(Environment.NewLine); Console.WriteLine("Entering [ {0} ] ...", args.Method); base.OnEntry(args); } public override void OnExit(MethodExecutionArgs args) { Console.WriteLine("Leaving [ {0} ] ...", args.Method); base.OnExit(args); } }
異常處理的類別程式碼如下所示。
[Serializable] public class ExceptionAttribute : OnExceptionAspect { public override void OnException(MethodExecutionArgs args) { Console.WriteLine(String.Format("Exception in :[{0}] , Message:[{1}]", args.Method, args.Exception.Message)); args.FlowBehavior = FlowBehavior.Continue; base.OnException(args); } }
計時處理的Aspect類別程式碼如下所示。
[Serializable] [MulticastAttributeUsage(MulticastTargets.Method)] public class TimingAttribute : PostSharp.Aspects.OnMethodBoundaryAspect { [NonSerialized] Stopwatch _StopWatch; public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args) { _StopWatch = Stopwatch.StartNew(); base.OnEntry(args); } public override void OnExit(PostSharp.Aspects.MethodExecutionArgs args) { Console.WriteLine(string.Format("[{0}] took {1}ms to execute", new StackTrace().GetFrame(1).GetMethod().Name, _StopWatch.ElapsedMilliseconds)); base.OnExit(args); } }
事務處理的Aspect類別程式碼如下所示。
[Serializable] [AspectTypeDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, typeof(LogAttribute))] public class RunInTransactionAttribute : OnMethodBoundaryAspect { [NonSerialized] TransactionScope TransactionScope; public override void OnEntry(MethodExecutionArgs args) { this.TransactionScope = new TransactionScope(TransactionScopeOption.RequiresNew); } public override void OnSuccess(MethodExecutionArgs args) { this.TransactionScope.Complete(); } public override void OnException(MethodExecutionArgs args) { args.FlowBehavior = FlowBehavior.Continue; Transaction.Current.Rollback(); Console.WriteLine("Transaction Was Unsuccessful!"); } public override void OnExit(MethodExecutionArgs args) { this.TransactionScope.Dispose(); } }
下面是幾個Aspect類別的切面處理程式碼,如下所示。
[Exception] [Log] static void Calc() { throw new pideByZeroException("A Math Error Occured..."); } [Log, Timing] static void LongRunningCalc() { //wait for 1000 miliseconds Thread.Sleep(1000); }
從上面我們可以看到,常規的異常處理、日誌處理都已經透過Attribute的方式進行處理了,在函數體裡面都只是剩下具體的業務邏輯程式碼了,這樣大大提高了程式碼的可讀性,簡潔明了。
運行上面的程式碼函數的調用,我們可以在輸出日誌裡面看到具體的結果內容。
Entering [ Void Calc() ] ... “System.pideByZeroException”类型的第一次机会异常在 PostSharpExample.exe 中发生 Exception in :[Void Calc()] , Message:[A Math Error Occured...] Leaving [ Void Calc() ] ... Entering [ Void LongRunningCalc() ] ... Leaving [ Void LongRunningCalc() ] ... [LongRunningCalc] took 1002ms to execute
这样,通过声明的方式,就实现了常规日志 、异常的处理,当然实际项目上使用日志、异常处理的这些代码肯定会更加复杂一些,不过小例子已经实现了切面逻辑的分离处理了,尘归尘、土归土,一切都是那么的简洁安静了。
以上是在.NET專案中使用PostSharp的詳細內容。更多資訊請關注PHP中文網其他相關文章!