前段時間做一個權限系統,在考慮日志、異常接管、事務、緩存等等一些橫向組件的架構分析,自然就想用AOP技術實現;這兩天又重 新學習研究了PostSharp的架構與實現思想,將其融入現有框架;
由於采用 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兩部分,相當精彩,本文就是在參考這兩篇好文的基礎上做的。
我們假設有這麼個場景,其實這也是實際業務中很常見的處理方式:有一定單管理模塊,具備新增、刪除兩功能,我們在新增刪除的時 候必須校驗權限,在刪除的時候還必須記錄日志,出現異常了還必須捕捉並記錄異常;
按以前的寫法我們可能很麻煩,我們要如此這般的寫:
01 public class XOrders
02 {
03 public bool Add(string id, string orderName)
04 {
05 try
06 {
07 if (User.AddEnable)
08 {
09 //TODO:新增訂單的實現
10 Console.WriteLine("正在執行新增訂單方法的操作,回車繼續……");
11 Console.ReadLine();
12 Console.WriteLine("您添加訂單成功:編號:{0},名稱:{1}", id, orderName);
13 return true;
14 }
15 else
16 {
17 //
18 }
19 }
20 catch (Exception)
21 {
22 //TODO:記錄異常的實現
23 throw;
24 }
25
26 return true;
27
28 }
29
30 public bool Delete(string id)
31 {
32 try
33 {
34 if (User.DeleteEnable)
35 {
36 //TODO:刪除訂單的實現
37 Console.WriteLine("您刪除訂單成功:編號:{0}", id);
38 }
39 else
40 {
41 //
42 }
43
44 }
45 catch (Exception)
46 {
47 //TODO:記錄異常的實現
48 throw;
49 }
50
51 return true;
52 }
這種寫的弊端我就不多說了,有很多先驅都闡述過……
我要演示的是采用AOP技術的框架原型實現:
首先我們應該安裝PostSharp(一定要安裝要不能沒辦法注入處理代碼)
然後我們實現Orders對象
01 using System;
02
03 namespace PostSharp.Demo
04 {
05 public class Orders
06 {
07 [Permission]
08 [Exception]
09 public bool Add(string id, string orderName)
10 {
11 Console.WriteLine("正在執行新增訂單方法的操作,回車繼續……");
12 Console.ReadLine();
13 Console.WriteLine("您添加訂單成功:編號:{0},名稱:{1}", id, orderName);
14 return true;
15 }
16
17 [Logger]
18 [Permission]
19 [Exception]
20 public bool Delete(string id)
21 {
22 Console.WriteLine("您刪除訂單成功:編號:{0}", id);
23
24 return true;
25 }
26 }
27 }
當然還要模擬一個用戶資格認證
01 namespace PostSharp.Demo
02 {
03 /// <summary>
04 /// 靜態的用戶對象,用於存放當前登錄用戶,成員資格
05 /// </summary>
06 public static class User
07 {
08 private static string _userId;
09
10 public static string UserId
11 {
12 get { return _userId; }
13 set { _userId = value; }
14 }
15
16 public static bool AddEnable
17 {
18 get
19 {
20 return (_userId.ToLower() == "admin");
21 }
22 }
23
24 public static bool DeleteEnable
25 {
26 get
27 {
28 return (_userId.ToLower() == "admin");
29 }
30 }
31 }
32 }
再然後我們實現權限控制方面PermissionAttribute,日志方面LoggerAttribute,異常處理方面ExceptionAttribute……
PermissionAttribute
01 using System;
02 using PostSharp.Laos;
03
04 namespace PostSharp.Demo
05 {
06 [Serializable]
07 [global::System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
08 public class PermissionAttribute : OnMethodBoundaryAspect
09 {
10 public override void OnEntry(MethodExecutionEventArgs eventArgs)
11 {
12 if (!User.AddEnable)
13 {
14 Console.WriteLine("用戶:【{0}】沒有權限:【{1}】", User.UserId, eventArgs.Method);
15 eventArgs.FlowBehavior = FlowBehavior.Return;
16 }
17
18 }
19 }
20 }
21
22 LoggerAttribute
23
24 using System;
25 using PostSharp.Laos;
26
27 namespace PostSharp.Demo
28 {
29 [Serializable]
30 [global::System.AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
31 public sealed class LoggerAttribute : OnMethodInvocationAspect
32 {
33 public override void OnInvocation(MethodInvocationEventArgs eventArgs)
34 {
35 DateTime time = DateTime.Now;
36 string log = "
然後再用控制台程序測試下能不能成功
01 Orders order = new Orders();
02 Console.WriteLine("請輸入用戶名:");
03 User.UserId = Console.ReadLine();
04 Console.WriteLine("請輸入密碼:");
05 Console.ReadLine();
06 string id;
07
08 LRedo:
09 Console.WriteLine("請輸入您要執行的操作:新增(A),刪除(D),退出(X)");
10
11 string opt = Console.ReadLine();
12
13 if (opt.ToLower() == "a")
14 {
15 Console.WriteLine("請輸入訂單編號:");
16 id = Console.ReadLine();
17
18 Console.WriteLine("請輸入訂單名稱:");
19 string name = Console.ReadLine();
20 order.Add(id, name);
21 }
22 else if (opt.ToLower() == "d")
23 {
24 Console.WriteLine("請輸入訂單編號:");
25 id = Console.ReadLine();
26 order.Delete(id);
27 }
28 else if (opt.ToLower() == "x")
29 {
30 }
31 else
32 {
33 Console.WriteLine("您的輸入不正確,請重新輸入!");
34 goto LRedo;
35 }
36
37 Console.WriteLine("按任意鍵退出……");
38 Console.ReadLine();
寫完這些我們再反編譯一下生成的exe文件,發現裡面的Orders成了這模樣了
001 public class Orders
002 {
003 // Methods
004 static Orders()
005 {
006 if (!~PostSharp~Laos~Implementation.initialized)
007 {
008 LaosNotInitializedException.Throw();
009 }
010 ~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(Orders.Add);
011 ~PostSharp~Laos~Implementation.~aspect~1.RuntimeInitialize (~PostSharp~Laos~Implementation.~targetMethod~1);
012 ~PostSharp~Laos~Implementation.~targetMethod~5 = methodof(Orders.Delete);
013 ~PostSharp~Laos~Implementation.~aspect~5.RuntimeInitialize (~PostSharp~Laos~Implementation.~targetMethod~5);
014 ~PostSharp~Laos~Implementation.~targetMethod~4 = methodof(Orders.Delete);
015 ~PostSharp~Laos~Implementation.~aspect~4.RuntimeInitialize (~PostSharp~Laos~Implementation.~targetMethod~4);
016 ~PostSharp~Laos~Implementation.~targetMethod~3 = methodof(Orders.Add);
017 ~PostSharp~Laos~Implementation.~aspect~3.RuntimeInitialize (~PostSharp~Laos~Implementation.~targetMethod~3);
018 ~PostSharp~Laos~Implementation.~targetMethod~2 = methodof(Orders.Delete);
019 ~PostSharp~Laos~Implementation.~aspect~2.RuntimeInitialize (~PostSharp~Laos~Implementation.~targetMethod~2);
020 }
021
022 private bool ~Delete(string id)
023 {
024 Console.WriteLine("您刪除訂單成功:編號:{0}", id);
025 return true;
026 }
027
028 public bool Add(string id, string orderName)
029 {
030 bool ~returnValue~1;
031 MethodExecutionEventArgs ~laosEventArgs~7;
032 try
033 {
034 object[] ~arguments~6 = new object[] { id, orderName };
035 ~laosEventArgs~7 = new MethodExecutionEventArgs(methodof(Orders.Add, Orders), this, ~arguments~6);
036 ~PostSharp~Laos~Implementation.~aspect~1.OnEntry(~laosEventArgs~7);
037 if (~laosEventArgs~7.FlowBehavior == FlowBehavior.Return)
038 {
039 return (bool) ~laosEventArgs~7.ReturnValue;
040 }
041 try
042 {
043 Console.WriteLine("正在執行新增訂單方法的操作,回車繼續……");
044 Console.ReadLine();
045 Console.WriteLine("您添加訂單成功:編號:{0},名稱:{1}", id, orderName);
046 ~returnValue~1 = true;
047 }
048 catch (Exception ~exception~2)
049 {
050 object[] ~arguments~3 = new object[] { id, orderName };
051 MethodExecutionEventArgs ~laosEventArgs~4 = new MethodExecutionEventArgs (methodof(Orders.Add, Orders), this, ~arguments~3);
052 ~laosEventArgs~4.Exception = ~exception~2;
053 ~PostSharp~Laos~Implementation.~aspect~3.OnException(~laosEventArgs~4);
054 switch (~laosEventArgs~4.FlowBehavior)
055 {
056 case FlowBehavior.Continue:
057 goto Label_0145;
058
059 case FlowBehavior.Return:
060 ~returnValue~1 = (bool) ~laosEventArgs~4.ReturnValue;
061 goto Label_0145;
062 }
063 throw;
064 }
065 Label_0145:
066 ~laosEventArgs~7.ReturnValue = ~returnValue~1;
067 ~PostSharp~Laos~Implementation.~aspect~1.OnSuccess(~laosEventArgs~7);
068 ~returnValue~1 = (bool) ~laosEventArgs~7.ReturnValue;
069 }
070 catch (Exception ~exception~5)
071 {
072 ~laosEventArgs~7.Exception = ~exception~5;
073 ~PostSharp~Laos~Implementation.~aspect~1.OnException(~laosEventArgs~7);
074 switch (~laosEventArgs~7.FlowBehavior)
075 {
076 case FlowBehavior.Continue:
077 return ~returnValue~1;
078
079 case FlowBehavior.Return:
080 return (bool) ~laosEventArgs~7.ReturnValue;
081 }
082 throw;
083 }
084 finally
085 {
086 ~laosEventArgs~7.ReturnValue = ~returnValue~1;
087 ~PostSharp~Laos~Implementation.~aspect~1.OnExit(~laosEventArgs~7);
088 ~returnValue~1 = (bool) ~laosEventArgs~7.ReturnValue;
089 }
090 return ~returnValue~1;
091 }
092
093 [DebuggerNonUserCode]
094 public bool Delete(string id)
095 {
096 bool ~returnValue~2;
097 MethodExecutionEventArgs ~laosEventArgs~8;
098 try
099 {
100 object[] ~arguments~7 = new object[] { id };
101 ~laosEventArgs~8 = new MethodExecutionEventArgs(methodof(Orders.Delete, Orders), this, ~arguments~7);
102 ~PostSharp~Laos~Implementation.~aspect~2.OnEntry(~laosEventArgs~8);
103 if (~laosEventArgs~8.FlowBehavior == FlowBehavior.Return)
104 {
105 return (bool) ~laosEventArgs~8.ReturnValue;
106 }
107 try
108 {
109 Delegate delegateInstance = new ~PostSharp~Laos~Implementation.~delegate~0 (this.~Delete);
110 object[] arguments = new object[] { id };
111 MethodInvocationEventArgs eventArgs = new MethodInvocationEventArgs (delegateInstance, arguments);
112 ~PostSharp~Laos~Implementation.~aspect~5.OnInvocation(eventArgs);
113 ~returnValue~2 = (bool) eventArgs.ReturnValue;
114 }
115 catch (Exception ~exception~3)
116 {
117 object[] ~arguments~4 = new object[] { id };
118 MethodExecutionEventArgs ~laosEventArgs~5 = new MethodExecutionEventArgs (methodof(Orders.Delete, Orders), this, ~arguments~4);
119 ~laosEventArgs~5.Exception = ~exception~3;
120 ~PostSharp~Laos~Implementation.~aspect~4.OnException(~laosEventArgs~5);
121 switch (~laosEventArgs~5.FlowBehavior)
122 {
123 case FlowBehavior.Continue:
124 goto Label_0160;
125
126 case FlowBehavior.Return:
127 ~returnValue~2 = (bool) ~laosEventArgs~5.ReturnValue;
128 goto Label_0160;
129 }
130 throw;
131 }
132 Label_0160:
133 ~laosEventArgs~8.ReturnValue = ~returnValue~2;
134 ~PostSharp~Laos~Implementation.~aspect~2.OnSuccess(~laosEventArgs~8);
135 ~returnValue~2 = (bool) ~laosEventArgs~8.ReturnValue;
136 }
137 catch (Exception ~exception~6)
138 {
139 ~laosEventArgs~8.Exception = ~exception~6;
140 ~PostSharp~Laos~Implementation.~aspect~2.OnException(~laosEventArgs~8);
141 switch (~laosEventArgs~8.FlowBehavior)
142 {
143 case FlowBehavior.Continue:
144 return ~returnValue~2;
145
146 case FlowBehavior.Return:
147 return (bool) ~laosEventArgs~8.ReturnValue;
148 }
149 throw;
150 }
151 finally
152 {
153 ~laosEventArgs~8.ReturnValue = ~returnValue~2;
154 ~PostSharp~Laos~Implementation.~aspect~2.OnExit(~laosEventArgs~8);
155 ~returnValue~2 = (bool) ~laosEventArgs~8.ReturnValue;
156 }
157 return ~returnValue~2;
158 }
159 }
這裡可以看出,如果是在AOP的異常捕獲裡面標記 eventArgs.FlowBehavior = FlowBehavior.Return,那麼標記的方法將不會執行。