AOP已經不是一個什麼新名詞了,在博客園使用關鍵字搜索可以查出n多條關於AOP的介紹,這裏就不再贅述了。 在Bruce Zhang's Blog裏面有很多關於AOP介紹及其在.net下實現研究,總覺得如果什麼都從頭來寫難免有自造輪子的嫌疑,但是目前也沒有很成熟的AOP框架讓我們能輕鬆完成基於AOP架構,不過一直以來都在關注的PostSharp開源項目日趨成熟,目前已發佈了PostSharp 1.0 (Beta release 3)。即使如此,也還沒能到應用到產品上的時候。 前段時間一直在封裝一個權限系統,時常爲如何給調用方提供一個良好的編程接口煩惱,加之前前段時間考慮的日誌、異常接管、事務、緩存等等一些橫向組件的架構分析,自然就想用AOP技術實現,但是由於實現難度實在不小作罷;這兩天又重新學習研究了PostSharp的架構與實現思想,覺得還是嘗試一下,將其融入現有框架; 早在年初就有不少前輩大師就如何使用這個東西撰寫過文章,如Q.yuhen的PostSharp - Lightweight Aspect-Oriented System該仁兄下面見解很到位: 和以往基於 Dynamic Proxy 方式與 AOP 解決方案做個比較。 由於採用 MSIL Injection,因此靜態代碼注入的執行效率要高於使用 Reflection Emit。 使用 MSBuild Task,使得開發人員可以像使用編譯器內置 Attribute 那樣使用 AOP。 可以攔截任意方法,而 Dynamic Proxy 方式的 AOP 往往採取繼承方式來攔截 Virtual 方法。 擁有更多的控制權。包括中斷執行流程,修改參數和返回值等等。 還可以攔截 Field Access、Exception 等操作。 無需將對象創建代碼改成 "new proxy()",更加透明。 可以使用通配符進行多重攔截匹配。 靜態注入帶來的問題更多的是注入代碼的質量和調試複雜度。 另外有一老外的Using AOP and PostSharp to Enhance Your CodeAB兩部分,相當精彩,本文就是在參考這兩篇好文的基礎上做的。 我們假設有這麼個場景,其實這也是實際業務中很常見的處理方式:有一定單管理模塊,具備新增、刪除兩功能,我們在新增刪除的時候必須校驗權限,在刪除的時候還必須記錄日誌,出現異常了還必須捕捉並記錄異常; 按以前的寫法我們可能很麻煩,我們要如此這般的寫: public class XOrders { public bool Add(string id, string orderName) { try { if (User.AddEnable) { //TODO:新增訂單的實現 Console.WriteLine("正在執行新增訂單方法的操作,回車繼續……"); Console.ReadLine(); Console.WriteLine("您添加訂單成功:編號:{0},名稱:{1}", id, orderName); return true; } else { // } } catch (Exception) { //TODO:記錄異常的實現 throw; } return true; } public bool Delete(string id) { try { if (User.DeleteEnable) { //TODO:刪除訂單的實現 Console.WriteLine("您刪除訂單成功:編號:{0}", id); } else { // } } catch (Exception) { //TODO:記錄異常的實現 throw; } return true; } 這種寫的弊端我就不多說了,有很多先驅都闡述過…… 我要演示的是採用AOP技術的框架原型實現: 首先我們應該安裝PostSharp(一定要安裝要不能沒辦法注入處理代碼) 然後我們實現Orders對象 using System; namespace PostSharp.Demo { public class Orders { [Permission] [Exception] public bool Add(string id, string orderName) { Console.WriteLine("正在執行新增訂單方法的操作,回車繼續……"); Console.ReadLine(); Console.WriteLine("您添加訂單成功:編號:{0},名稱:{1}", id, orderName); return true; } [Logger] [Permission] [Exception] public bool Delete(string id) { Console.WriteLine("您刪除訂單成功:編號:{0}", id); return true; } } } 當然還要模擬一個用戶資格認證 namespace PostSharp.Demo { /// <summary> /// 靜態的用戶對象,用於存放當前登錄用戶,成員資格 /// </summary> public static class User { private static string _userId; public static string UserId { get { return _userId; } set { _userId = value; } } public static bool AddEnable { get { return (_userId.ToLower() == "admin"); } } public static bool DeleteEnable { get { return (_userId.ToLower() == "admin"); } } } } 再然後我們實現權限控制方面PermissionAttribute,日誌方面LoggerAttribute,異常處理方面ExceptionAttribute…… PermissionAttribute using System; using PostSharp.Laos; namespace PostSharp.Demo { [Serializable] [global::System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] public class PermissionAttribute : OnMethodBoundaryAspect { public override void OnEntry(MethodExecutionEventArgs eventArgs) { if (!User.AddEnable) { Console.WriteLine("用戶:【{0}】沒有權限:【{1}】", User.UserId, eventArgs.Method); eventArgs.FlowBehavior = FlowBehavior.Return; } } } } LoggerAttribute using System; using PostSharp.Laos; namespace PostSharp.Demo { [Serializable] [global::System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] public sealed class LoggerAttribute : OnMethodInvocationAspect { public override void OnInvocation(MethodInvocationEventArgs eventArgs) { DateTime time = DateTime.Now; string log = "時間:{0},操作人員:{1},操作:{2}!"; object[] arg = eventArgs.GetArguments(); log = String.Format(log, time, User.UserId, "刪除Id爲" + arg[0].ToString() + "的訂單!"); System.IO.File.WriteAllText("C://Log.Txt", log); } } } ExceptionAttribute using System; using PostSharp.Laos; namespace PostSharp.Demo { [Serializable] [global::System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] public class ExceptionAttribute : OnExceptionAspect { public override void OnException(MethodExecutionEventArgs eventArgs) { Console.WriteLine("程序出現異常:{0}", eventArgs.Exception.Message); eventArgs.FlowBehavior = FlowBehavior.Return; } } } 然後再用控制檯程序測試下能不能成功 Orders order = new Orders(); Console.WriteLine("請輸入用戶名:"); User.UserId = Console.ReadLine(); Console.WriteLine("請輸入密碼:"); Console.ReadLine(); string id; LRedo: Console.WriteLine("請輸入您要執行的操作:新增(A),刪除(D),退出(X)"); string opt = Console.ReadLine(); if (opt.ToLower() == "a") { Console.WriteLine("請輸入訂單編號:"); id = Console.ReadLine(); Console.WriteLine("請輸入訂單名稱:"); string name = Console.ReadLine(); order.Add(id, name); } else if (opt.ToLower() == "d") { Console.WriteLine("請輸入訂單編號:"); id = Console.ReadLine(); order.Delete(id); } else if (opt.ToLower() == "x") { } else { Console.WriteLine("您的輸入不正確,請重新輸入!"); goto LRedo; } Console.WriteLine("按任意鍵退出……"); Console.ReadLine(); 寫完這些我們再反編譯一下生成的exe文件,發現裏面的Orders成了這模樣了,神奇了? public class Orders { // Methods static Orders() { if (!~PostSharp~Laos~Implementation.initialized) { LaosNotInitializedException.Throw(); } ~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(Orders.Add); ~PostSharp~Laos~Implementation.~aspect~1.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~1); ~PostSharp~Laos~Implementation.~targetMethod~5 = methodof(Orders.Delete); ~PostSharp~Laos~Implementation.~aspect~5.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~5); ~PostSharp~Laos~Implementation.~targetMethod~4 = methodof(Orders.Delete); ~PostSharp~Laos~Implementation.~aspect~4.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~4); ~PostSharp~Laos~Implementation.~targetMethod~3 = methodof(Orders.Add); ~PostSharp~Laos~Implementation.~aspect~3.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~3); ~PostSharp~Laos~Implementation.~targetMethod~2 = methodof(Orders.Delete); ~PostSharp~Laos~Implementation.~aspect~2.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~2); } private bool ~Delete(string id) { Console.WriteLine("您刪除訂單成功:編號:{0}", id); return true; } public bool Add(string id, string orderName) { bool ~returnValue~1; MethodExecutionEventArgs ~laosEventArgs~7; try { object[] ~arguments~6 = new object[] { id, orderName }; ~laosEventArgs~7 = new MethodExecutionEventArgs(methodof(Orders.Add, Orders), this, ~arguments~6); ~PostSharp~Laos~Implementation.~aspect~1.OnEntry(~laosEventArgs~7); if (~laosEventArgs~7.FlowBehavior == FlowBehavior.Return) { return (bool) ~laosEventArgs~7.ReturnValue; } try { Console.WriteLine("正在執行新增訂單方法的操作,回車繼續……"); Console.ReadLine(); Console.WriteLine("您添加訂單成功:編號:{0},名稱:{1}", id, orderName); ~returnValue~1 = true; } catch (Exception ~exception~2) { object[] ~arguments~3 = new object[] { id, orderName }; MethodExecutionEventArgs ~laosEventArgs~4 = new MethodExecutionEventArgs(methodof(Orders.Add, Orders), this, ~arguments~3); ~laosEventArgs~4.Exception = ~exception~2; ~PostSharp~Laos~Implementation.~aspect~3.OnException(~laosEventArgs~4); switch (~laosEventArgs~4.FlowBehavior) { case FlowBehavior.Continue: goto Label_0145; case FlowBehavior.Return: ~returnValue~1 = (bool) ~laosEventArgs~4.ReturnValue; goto Label_0145; } throw; } Label_0145: ~laosEventArgs~7.ReturnValue = ~returnValue~1; ~PostSharp~Laos~Implementation.~aspect~1.OnSuccess(~laosEventArgs~7); ~returnValue~1 = (bool) ~laosEventArgs~7.ReturnValue; } catch (Exception ~exception~5) { ~laosEventArgs~7.Exception = ~exception~5; ~PostSharp~Laos~Implementation.~aspect~1.OnException(~laosEventArgs~7); switch (~laosEventArgs~7.FlowBehavior) { case FlowBehavior.Continue: return ~returnValue~1; case FlowBehavior.Return: return (bool) ~laosEventArgs~7.ReturnValue; } throw; } finally { ~laosEventArgs~7.ReturnValue = ~returnValue~1; ~PostSharp~Laos~Implementation.~aspect~1.OnExit(~laosEventArgs~7); ~returnValue~1 = (bool) ~laosEventArgs~7.ReturnValue; } return ~returnValue~1; } [DebuggerNonUserCode] public bool Delete(string id) { bool ~returnValue~2; MethodExecutionEventArgs ~laosEventArgs~8; try { object[] ~arguments~7 = new object[] { id }; ~laosEventArgs~8 = new MethodExecutionEventArgs(methodof(Orders.Delete, Orders), this, ~arguments~7); ~PostSharp~Laos~Implementation.~aspect~2.OnEntry(~laosEventArgs~8); if (~laosEventArgs~8.FlowBehavior == FlowBehavior.Return) { return (bool) ~laosEventArgs~8.ReturnValue; } try { Delegate delegateInstance = new ~PostSharp~Laos~Implementation.~delegate~0(this.~Delete); object[] arguments = new object[] { id }; MethodInvocationEventArgs eventArgs = new MethodInvocationEventArgs(delegateInstance, arguments); ~PostSharp~Laos~Implementation.~aspect~5.OnInvocation(eventArgs); ~returnValue~2 = (bool) eventArgs.ReturnValue; } catch (Exception ~exception~3) { object[] ~arguments~4 = new object[] { id }; MethodExecutionEventArgs ~laosEventArgs~5 = new MethodExecutionEventArgs(methodof(Orders.Delete, Orders), this, ~arguments~4); ~laosEventArgs~5.Exception = ~exception~3; ~PostSharp~Laos~Implementation.~aspect~4.OnException(~laosEventArgs~5); switch (~laosEventArgs~5.FlowBehavior) { case FlowBehavior.Continue: goto Label_0160; case FlowBehavior.Return: ~returnValue~2 = (bool) ~laosEventArgs~5.ReturnValue; goto Label_0160; } throw; } Label_0160: ~laosEventArgs~8.ReturnValue = ~returnValue~2; ~PostSharp~Laos~Implementation.~aspect~2.OnSuccess(~laosEventArgs~8); ~returnValue~2 = (bool) ~laosEventArgs~8.ReturnValue; } catch (Exception ~exception~6) { ~laosEventArgs~8.Exception = ~exception~6; ~PostSharp~Laos~Implementation.~aspect~2.OnException(~laosEventArgs~8); switch (~laosEventArgs~8.FlowBehavior) { case FlowBehavior.Continue: return ~returnValue~2; case FlowBehavior.Return: return (bool) ~laosEventArgs~8.ReturnValue; } throw; } finally { ~laosEventArgs~8.ReturnValue = ~returnValue~2; ~PostSharp~Laos~Implementation.~aspect~2.OnExit(~laosEventArgs~8); ~returnValue~2 = (bool) ~laosEventArgs~8.ReturnValue; } return ~returnValue~2; } }