PostSharp 是一個令人興奮的項目,他結合了 MSBuild Task 和 MSIL Injection 技術,從另外一個角度實現 AOP 編程。試用過後你會感覺到其便利性,我們和以往基於 Dynamic Proxy 方式的 AOP 解決方案做個比較。
輸出:
OnEntry:MyClass.Int32 Test(Int32)
i=123
Test:123
OnExit...
整個過程非常簡單:
1. 創建一個繼承自 OnMethodBoundaryAspect 的特性類,用於攔截方法。
2. override 特定的方法,執行攔截操作。方法參數提供了各種操作能力。
3. 向目標方法添加特性。
4. 編譯,執行。
或許你會好奇,AopMethodAttribute 是如何被創建實例,並被執行的呢?
1. 注意編譯時 VS2005 Output 窗口的輸出內容,你會看到如下內容。其實 PostSharp 提供了一個 MSBuild Task,在我們每次編譯時會調用它執行一些注入操作。
2. 用反編譯工具看看注入後的方法內容。看完這些代碼,就算我不說,你也能明白它是如何實現攔截的了。
我們繼續寫一點其它的例子,諸如攔截屬性訪問和異常。
- 由於採用 MSIL Injection,因此靜態代碼注入的執行效率要高於使用 Reflection Emit。
- 使用 MSBuild Task,使得開發人員可以像使用編譯器內置 Attribute 那樣使用 AOP。
- 可以攔截任意方法,而 Dynamic Proxy 方式的 AOP 往往採取繼承方式來攔截 Virtual 方法。
- 擁有更多的控制權。包括中斷執行流程,修改參數和返回值等等。
- 還可以攔截 Field Access、Exception 等操作。
- 無需將對象創建代碼改成 "new proxy()",更加透明。
- 可以使用通配符進行多重攔截匹配。
- 靜態注入帶來的問題更多的是注入代碼的質量和調試複雜度。
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using PostSharp.Laos;
[Serializable]
public class AopMethodAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
// 獲取方法名稱
Console.WriteLine("OnEntry:{0}.{1}", eventArgs.Method.ReflectedType, eventArgs.Method);
// 顯示方法參數
ParameterInfo[] ps = eventArgs.Method.GetParameters();
object[] pv = eventArgs.GetArguments();
for (int i = 0; i < ps.Length; i++)
{
Console.WriteLine(" {0}={1}", ps[i].Name, pv[i]);
}
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine("OnExit");
}
}
public class MyClass
{
[AopMethod]
public int Test(int i)
{
Console.WriteLine("Test:{0}", i);
return i++;
}
}
MyClass o = new MyClass();
o.Test(123);
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using PostSharp.Laos;
[Serializable]
public class AopMethodAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
// 獲取方法名稱
Console.WriteLine("OnEntry:{0}.{1}", eventArgs.Method.ReflectedType, eventArgs.Method);
// 顯示方法參數
ParameterInfo[] ps = eventArgs.Method.GetParameters();
object[] pv = eventArgs.GetArguments();
for (int i = 0; i < ps.Length; i++)
{
Console.WriteLine(" {0}={1}", ps[i].Name, pv[i]);
}
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine("OnExit");
}
}
public class MyClass
{
[AopMethod]
public int Test(int i)
{
Console.WriteLine("Test:{0}", i);
return i++;
}
}
MyClass o = new MyClass();
o.Test(123);
輸出:
OnEntry:MyClass.Int32 Test(Int32)
i=123
Test:123
OnExit...
整個過程非常簡單:
1. 創建一個繼承自 OnMethodBoundaryAspect 的特性類,用於攔截方法。
2. override 特定的方法,執行攔截操作。方法參數提供了各種操作能力。
3. 向目標方法添加特性。
4. 編譯,執行。
或許你會好奇,AopMethodAttribute 是如何被創建實例,並被執行的呢?
1. 注意編譯時 VS2005 Output 窗口的輸出內容,你會看到如下內容。其實 PostSharp 提供了一個 MSBuild Task,在我們每次編譯時會調用它執行一些注入操作。
PostSharp 1.0 [1.0.4.162] - Copyright (c) Gael Fraiteur and Community, 2006.
2. 用反編譯工具看看注入後的方法內容。看完這些代碼,就算我不說,你也能明白它是如何實現攔截的了。
static MyClass()
{
if (!~PostSharp~Laos~Implementation.initialized)
{
throw new LaosNotInitializedException();
}
~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(MyClass.Test);
~PostSharp~Laos~Implementation.~aspect~1.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~1);
}
public int Test(int i)
{
int returnValue;
MethodExecutionEventArgs eventArgs;
try
{
object[] arguments = new object[] { i };
eventArgs = new MethodExecutionEventArgs(methodof(MyClass.Test, MyClass), this, arguments);
~PostSharp~Laos~Implementation.~aspect~1.OnEntry(eventArgs);
if (eventArgs.FlowBehavior == FlowBehavior.Return)
{
return (int) eventArgs.ReturnValue;
}
Console.WriteLine("Test:{0}", i);
int num = i++;
returnValue = num;
eventArgs.ReturnValue = returnValue;
~PostSharp~Laos~Implementation.~aspect~1.OnSuccess(eventArgs);
returnValue = (int) eventArgs.ReturnValue;
}
catch (Exception exception)
{
eventArgs.Exception = exception;
~PostSharp~Laos~Implementation.~aspect~1.OnException(eventArgs);
switch (eventArgs.FlowBehavior)
{
case FlowBehavior.Continue:
return returnValue;
case FlowBehavior.Return:
return (int) eventArgs.ReturnValue;
}
throw;
}
finally
{
eventArgs.ReturnValue = returnValue;
~PostSharp~Laos~Implementation.~aspect~1.OnExit(eventArgs);
returnValue = (int) eventArgs.ReturnValue;
}
return returnValue;
}
我們可以使用通配符攔截更多的內容,以簡化我們的編碼。{
if (!~PostSharp~Laos~Implementation.initialized)
{
throw new LaosNotInitializedException();
}
~PostSharp~Laos~Implementation.~targetMethod~1 = methodof(MyClass.Test);
~PostSharp~Laos~Implementation.~aspect~1.RuntimeInitialize(~PostSharp~Laos~Implementation.~targetMethod~1);
}
public int Test(int i)
{
int returnValue;
MethodExecutionEventArgs eventArgs;
try
{
object[] arguments = new object[] { i };
eventArgs = new MethodExecutionEventArgs(methodof(MyClass.Test, MyClass), this, arguments);
~PostSharp~Laos~Implementation.~aspect~1.OnEntry(eventArgs);
if (eventArgs.FlowBehavior == FlowBehavior.Return)
{
return (int) eventArgs.ReturnValue;
}
Console.WriteLine("Test:{0}", i);
int num = i++;
returnValue = num;
eventArgs.ReturnValue = returnValue;
~PostSharp~Laos~Implementation.~aspect~1.OnSuccess(eventArgs);
returnValue = (int) eventArgs.ReturnValue;
}
catch (Exception exception)
{
eventArgs.Exception = exception;
~PostSharp~Laos~Implementation.~aspect~1.OnException(eventArgs);
switch (eventArgs.FlowBehavior)
{
case FlowBehavior.Continue:
return returnValue;
case FlowBehavior.Return:
return (int) eventArgs.ReturnValue;
}
throw;
}
finally
{
eventArgs.ReturnValue = returnValue;
~PostSharp~Laos~Implementation.~aspect~1.OnExit(eventArgs);
returnValue = (int) eventArgs.ReturnValue;
}
return returnValue;
}
[AopMethod(AttributeTargetMembers="T*")]
public class MyClass
{
public int T1(int i)
{
Console.WriteLine("T1:{0}", i);
return i++;
}
public void T2(string s)
{
Console.WriteLine("T2:{0}", s);
}
}
public class MyClass
{
public int T1(int i)
{
Console.WriteLine("T1:{0}", i);
return i++;
}
public void T2(string s)
{
Console.WriteLine("T2:{0}", s);
}
}
我們繼續寫一點其它的例子,諸如攔截屬性訪問和異常。
Field Aspect
[Serializable]
public class AopPropertyAttribute : OnFieldAccessAspect
{
public override void OnGetValue(FieldAccessEventArgs eventArgs)
{
Console.WriteLine("ExposedFieldValue:{0}; StoredFieldValue:{1}",
eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
}
public override void OnSetValue(FieldAccessEventArgs eventArgs)
{
Console.WriteLine("ExposedFieldValue:{0}; StoredFieldValue:{1}",
eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
}
}
public class MyClass
{
[AopProperty]
private int x;
public int X
{
get { return x; }
set { x = value; }
}
}
Exception Aspect
[Serializable]
public class AopExceptionAttribute : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine(eventArgs.Exception.Message);
eventArgs.FlowBehavior = FlowBehavior.Return;
}
}
public class MyClass
{
[AopException]
public void Test(int i)
{
throw new Exception("Error");
}
}
[Serializable]
public class AopPropertyAttribute : OnFieldAccessAspect
{
public override void OnGetValue(FieldAccessEventArgs eventArgs)
{
Console.WriteLine("ExposedFieldValue:{0}; StoredFieldValue:{1}",
eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
}
public override void OnSetValue(FieldAccessEventArgs eventArgs)
{
Console.WriteLine("ExposedFieldValue:{0}; StoredFieldValue:{1}",
eventArgs.ExposedFieldValue, eventArgs.StoredFieldValue);
}
}
public class MyClass
{
[AopProperty]
private int x;
public int X
{
get { return x; }
set { x = value; }
}
}
Exception Aspect
[Serializable]
public class AopExceptionAttribute : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine(eventArgs.Exception.Message);
eventArgs.FlowBehavior = FlowBehavior.Return;
}
}
public class MyClass
{
[AopException]
public void Test(int i)
{
throw new Exception("Error");
}
}
更詳細的信息,請訪問 PostSharp 網站,或者查閱其幫助文件。
結論:
1.方法執行的日誌記錄可以用AOP實現
2.表之間的級聯刪除可以用AOP實現
3.數據庫操作日誌可以用AOP記錄(因爲數據庫提供商不提供它的日誌,導致第三方數據庫同步無法便利實現)
...