上一節實現了動態代理,接下來 有時候,我不需要在每一個方法都要記錄日志,做權限驗證 等等。 所有就有了這樣的需求。AOP實現特定方法過濾,有選擇性的來對方法實現AOP 攔截。就是本節標題所示。
舉個例子,對於查詢的方法我不需要記錄日志,所以,我就找到如果以“Get”開頭的方法,就不記錄日志,否則就記錄日志;所以基於這樣一個需求,代碼如下:
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
if (!methodInfo.Name.StartsWith("Get"))
Log("In Dynamic Proxy - Before executing '{0}'",
methodCall.MethodName);
try
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
if (!methodInfo.Name.StartsWith("Get"))
Log("In Dynamic Proxy - After executing '{0}' ",
methodCall.MethodName);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
if (!methodInfo.Name.StartsWith("Get"))
Log(string.Format(
"In Dynamic Proxy- Exception {0} executing '{1}'", e),
methodCall.MethodName);
return new ReturnMessage(e, methodCall);
}
}
在上面的代碼里,有3處是代碼重復的,methodInfo.Name.StartsWith("Get"),對於代碼重復的,我們可以提取為方法,提取后的代碼如下:
private static bool IsValidMethod(MethodInfo methodInfo) { return !methodInfo.Name.StartsWith("Get"); }
現在你只需要修改一個地方,但是你還是的修改類的代碼,假如有一天,你的項目里需要自定過濾條件,此時最好的方法就是將Filter
定義為屬性供用戶自己來出來,所以這個需求的代碼 現在修改為如下:
class DynamicProxy<T> : RealProxy { private readonly T _decorated; private Predicate<MethodInfo> _filter; public DynamicProxy(T decorated) : base(typeof(T)) { _decorated = decorated; _filter = m => true; } public Predicate<MethodInfo> Filter { get { return _filter; } set { if (value == null) _filter = m => true; else _filter = value; } } private void Log(string msg, object arg = null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(msg, arg); Console.ResetColor(); } public override IMessage Invoke(IMessage msg) { var methodCall = msg as IMethodCallMessage; var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) Log("In Dynamic Proxy - Before executing '{0}'", methodCall.MethodName); try { var result = methodInfo.Invoke(_decorated, methodCall.InArgs); if (_filter(methodInfo)) Log("In Dynamic Proxy - After executing '{0}' ", methodCall.MethodName); return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall); } catch (Exception e) { if (_filter(methodInfo)) Log(string.Format( "In Dynamic Proxy- Exception {0} executing '{1}'", e), methodCall.MethodName); return new ReturnMessage(e, methodCall); } } }
基於這樣,用戶就可以自定義 那些方法需要AOP 那些方法不需要。 例如:
public class RepositoryFactory { public static IRepository<T> Create<T>() { var repository = new Repository<T>(); var dynamicProxy = new DynamicProxy<IRepository<T>>(repository) { Filter = m => !m.Name.StartsWith("Get") }; return dynamicProxy.GetTransparentProxy() as IRepository<T>; } } }
運行后的代碼如下圖:
Get 開頭的方法就沒記錄日志。
此外,如果你不想更改 業務類,你更改AOP 類,代碼如下:
class DynamicProxy<T> : RealProxy { private readonly T _decorated; private Predicate<MethodInfo> _filter; public event EventHandler<IMethodCallMessage> BeforeExecute; public event EventHandler<IMethodCallMessage> AfterExecute; public event EventHandler<IMethodCallMessage> ErrorExecuting; public DynamicProxy(T decorated) : base(typeof(T)) { _decorated = decorated; Filter = m => true; } public Predicate<MethodInfo> Filter { get { return _filter; } set { if (value == null) _filter = m => true; else _filter = value; } } private void OnBeforeExecute(IMethodCallMessage methodCall) { if (BeforeExecute != null) { var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) BeforeExecute(this, methodCall); } } private void OnAfterExecute(IMethodCallMessage methodCall) { if (AfterExecute != null) { var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) AfterExecute(this, methodCall); } } private void OnErrorExecuting(IMethodCallMessage methodCall) { if (ErrorExecuting != null) { var methodInfo = methodCall.MethodBase as MethodInfo; if (_filter(methodInfo)) ErrorExecuting(this, methodCall); } } public override IMessage Invoke(IMessage msg) { var methodCall = msg as IMethodCallMessage; var methodInfo = methodCall.MethodBase as MethodInfo; OnBeforeExecute(methodCall); try { var result = methodInfo.Invoke(_decorated, methodCall.InArgs); OnAfterExecute(methodCall); return new ReturnMessage( result, null, 0, methodCall.LogicalCallContext, methodCall); } catch (Exception e) { OnErrorExecuting(methodCall); return new ReturnMessage(e, methodCall); } } }
上面定義了3 事件,分別是 BeforeExecute, AfterExecute and ErrorExecuting ,他們分別被調用 OnBeforeExecute, OnAfterExecute and OnErrorExecuting
其他 OnBeforeExecute, OnAfterExecute and OnErrorExecuting 會驗證,如果存在事件處理函數,並且進行了方法過濾,他們就會被調用。所以一個設置事件的Repository Factory 定義如下:
public class RepositoryFactory { private static void Log(string msg, object arg = null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(msg, arg); Console.ResetColor(); } public static IRepository<T> Create<T>() { var repository = new Repository<T>(); var dynamicProxy = new DynamicProxy<IRepository<T>>(repository); dynamicProxy.BeforeExecute += (s, e) => Log( "Before executing '{0}'", e.MethodName); dynamicProxy.AfterExecute += (s, e) => Log( "After executing '{0}'", e.MethodName); dynamicProxy.ErrorExecuting += (s, e) => Log( "Error executing '{0}'", e.MethodName); dynamicProxy.Filter = m => !m.Name.StartsWith("Get"); return dynamicProxy.GetTransparentProxy() as IRepository<T>; } }
到此,你現在可以選擇在程序執行之前,執行之后,或者發生錯誤時,是否應用到AOP。
不是替代工具
使用AOP可以增加Code到應用貸程序的所有層,且沒有重復的代碼。我展示的例子至少通過一個基於裝飾者模式的普通代理類應用一個擁有事件和表達式過濾器的AOP到你的類。
如您所見,RealProxy類是一個靈活的類,你可以完全控制代碼,沒有外部依賴。然而,請注意,RealProxy不可能替代其他AOP工具,比如PostSharp。PostSharp使用一個完全不同的方法。將中間語言(IL)代碼post-compilation一步,而不是使用反射,所以它比RealProxy應該有更好的性能。相對於PostSharp, 你還必須做更多的工作來實現一個基於RealProxy。但是 PostSharp,您只需要創建方面類和一個屬性添加到類(或方法),並且這就是所有。
另一方面,RealProxy,你可以l完全控制你的源代碼,沒有外部依賴項,您可以擴展和定制你想要的。例如,如果您想應用只在一個方面有日志屬性的方法,你可以這樣做:
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
if (!methodInfo.CustomAttributes
.Any(a => a.AttributeType == typeof (LogAttribute)))
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
...
除此之外,使用的技術RealProxy(攔截代碼,允許程序來取代它)是強大的。例如,如果您想創建一個模擬框架,用於創建通用的模擬和測試的子類,您可以使用RealProxy類攔截所有調用,並將其替換為你自己的行為,但這另一篇文章的主題!
tks! 到此AOP 就告一段落了。