之前做AgentBooking時候,遇到兩個問題比較棘手,一個是異常的傳遞與捕獲:如何可以合理地在層層代碼調用中統一傳遞並統一捕獲異常。因為如果有一個做法,可以地方統一處理異常,可以使代碼減少很多try cath邏輯,也不同時刻關注該怎么拋異常,這樣代碼寫起來就比較happy。
第二個是Log的統一記錄。和第一個問題一樣,如果可以找到一種方法,可以統一記Log,不用再在代碼中時刻關注什么時候該怎么記Log,這肯定是一種更好的改進。
上兩個星期去Search了一下AOP(
面向切面編程),其思想很大程度上可以給我們提供幫助。AOP大概意思就是說,在代碼的編譯期或者運行期自動為程序插入額外的功能邏輯。其思想就是將主要業務邏輯和輔助功能分離出來。在MOE中有一個經典的應用場合:每次MOE在調用Web Service時候,都回家RQ序列號並保存到一個txt的Log文件中,所以這個調用webservice的方法既有主要業務邏輯(RQ的生成)的代碼,又有RQ入Log的輔助功能。如果應用AOP,則這個方法中只需要寫主要業務的邏輯,RQ入LOG的邏輯代碼可以交給AOP框架自動在編譯期動態(或靜態)“插入”到原來的代碼里面。
AOP實現手段大概有兩種,一是在編譯是對代碼的IL進行分析,然后插入(官方叫“織入”)額外功能的代碼。二是對方法調用進行攔截並執行額外代碼邏輯。
.NET中對方法調用進行攔截主要是通過context對象。參考資料:
AOP研究,
context與攔截
這里主要介紹一下context與方法攔截。(因為IL涉及到編譯器,太深奧,不明覺厲,而方法攔截除了AOP,感覺還可以用在其他地方)
在.NET中,每個App Domain(應用程序域,在.net中一個進程可以有N個線程,一個線程可以有N個App Domain)都至少有一個Context,當一個對象被new出來后,就是存在於context中。(我覺得context是更是一種編程思想,就是將資源都放在一個地方。典型的有HttpContext,就集中包含了每個HttpRequest訪問時的一些信息)。.net程序第一個context是一個默認context,每個context都有一個ContextID.可以通過Thread.CurrentContext來訪問當前Context。
.net里的對象從context的角度可以分為兩種:context-aglie object(上下文靈活對象)和context-bound object(上下文綁定對象)。
context-aglie object總是存在於這個對象的調用者所在的context中(我查了一下資料,大概是這個意思,但我還沒有驗證過是不是這樣)。對context-aglie object的調用是一種直覺引用,並不是通過代理的調用。這個很重要,因為直接的調用是不能被攔截的,只有通過“代理”的調用才能被攔截,見下文。
而另一種是context-bound object(上下文綁定對象)。如果一個對象繼承了ContextBoundObject ,就成了一個context-bound object。 context-bound object會被強制綁定到一個指定的context中。當一個調用者和被調用的對象處於不同的context中時,這個調用不是直接的引用,而是需要通過代理去進行的。正是因為有這個“代理”,才能在調用過程中做手腳,例如在調用這個對象的一個方法前或調用方法完畢后做一些額外的邏輯(如Log,或者方法執行的時間統計等等),聽起來很熟悉?在HttpModule中也是這種做法。

這里的“代理”有幾種:遠程代理(Remote Proxy),虛擬代理(Virtual Proxy),智能引用代理(Smart Reference Proxy).在.NET Remoting中,代理又分為透明代理(Transparent Proxy)和真實代理(Real Proxy)
方法調用的攔截就需要用到透明代理和真實代理。當一個對象調用另一個context里的對象(比如說調用該對象的某個方法),這個調用到達透明代理時會被轉換為一個Message,並將Message傳給Real Proxy。Real Proxy將Message傳到這個對象的過程中,Message會經歷由N個MessageSink(消息槽)組成的管道中。每個MessageSink可以對消息進行處理並再次拋給下一個MessageSink,一直將調用傳遞到那個Object為止。每個MessageSink中便是我們攔截方法調用的地方了。

提供一個基於Context攔截的Logger 日志組件Demo:
AOP Logger