使用Unity進行AOP對象攔截


Unity是一款知名的依賴注入容器,其支持通過自定義擴展來擴充功能。在Unity軟件包內默認包含了一個對象攔截(Interception)擴展定義。本篇文章將介紹如何使用對象攔截功能來幫助你分離橫切關注點(Separation of cross-cutting concerns)。

對象攔截簡介

對象攔截是一種AOP(Aspect-oriented programming)編程的實踐方法。其可幫助你保持業務類的純凈,而無需考慮諸如日志和緩存等外圍關注點。

在.NET中,實現AOP有多種方法。一種方式是采用編譯后處理方式,例如PostSharp。在編譯后,PostSharp通過修改IL代碼來諸如橫切代碼。

相反地,對象攔截是在運行時執行的,同時也意味着會有一些限制。依據不同的攔截器實現,會有如下這些約束:

  • 透明代理攔截器:需要定義接口,或者要求類繼承自MarshalByRefObject。
  • 接口攔截器:需要定義接口。
  • 虛方法攔截器:僅需要方法被定義成virtual方法。

對象攔截如何工作

當從Unity容器請求目標對象時,將不會獲取到已配置的類的實例。實際上,將得到一個動態生成的代理對象,或者一個衍生類。

如果調用代理對象的一個方法,將可以在被調用方法執行前或執行后執行一些額外行為的代碼。那些定義行為的類需要實現ICallHandler接口。通過這些行為定義,我們可以訪問方法調用的參數列表,可以吞噬異常,或者可以返回自定義的異常。

附帶提一下,在不使用Unity容器的條件下,也是可以使用Unity攔截器的。

示例:將異常和方法調用參數列表記錄到日志中

在下面的示例中,我們將創建兩個自定義的行為,都實現了ICallHandler接口:

  • LoggerCallHandler:將方法調用參數列表記錄到日志中。
  • ExceptionLoggerCallHandler:將方法調用參數列表和異常信息及調用棧記錄到日志中。

ExceptionLoggerCallHandler定義如下:

 1   internal class ExceptionLoggerCallHandler : ICallHandler
 2   {
 3     public IMethodReturn Invoke(
 4       IMethodInvocation input, GetNextHandlerDelegate getNext)
 5     {
 6       IMethodReturn result = getNext()(input, getNext);
 7       if (result.Exception != null)
 8       {
 9         Console.WriteLine("ExceptionLoggerCallHandler:");
10         Console.WriteLine("\tParameters:");
11         for (int i = 0; i < input.Arguments.Count; i++)
12         {
13           var parameter = input.Arguments[i];
14           Console.WriteLine(
15             string.Format("\t\tParam{0} -> {1}", i, parameter.ToString()));
16         }
17         Console.WriteLine();
18         Console.WriteLine("Exception occured: ");
19         Console.WriteLine(
20           string.Format("\tException -> {0}", result.Exception.Message));
21 
22         Console.WriteLine();
23         Console.WriteLine("StackTrace:");
24         Console.WriteLine(Environment.StackTrace);
25       }
26 
27       return result;
28     }
29 
30     public int Order { get; set; }
31   }

為了將行為應用到方法上,我們需要創建相應的HandlerAttribute來創建行為的實例。

1   internal class ExceptionLoggerAttribute : HandlerAttribute
2   {
3     public override ICallHandler CreateHandler(IUnityContainer container)
4     {
5       return new ExceptionLoggerCallHandler();
6     }
7   }

在這個示例中,我們創建一個簡單的計算器類。同時為了使用接口攔截功能,我們還需創建一個接口類型,這樣才能應用指定的行為:

1   public interface ICalculator
2   {
3     [Logger]
4     int Add(int first, int second);
5 
6     [ExceptionLogger]
7     int Multiply(int first, int second);
8   }

計算器類的實現還和常規的一樣。現在我們需要配置Unity容器:

 1       IUnityContainer container = new UnityContainer();
 2       container.AddNewExtension<Interception>();
 3       container
 4         .RegisterType<ICalculator, Calculator>()
 5         .Configure<Interception>()
 6         .SetInterceptorFor<ICalculator>(new InterfaceInterceptor());
 7 
 8       // Resolve
 9       ICalculator calc = container.Resolve<ICalculator>();
10 
11       // Call method
12       calc.Add(1, 2);

當在容器上通過調用Resolve方法來嘗試獲得一個Calculate類的實例時,將會得到一個代理類實例。它的名字可能類似於 ‘DynamicModule.ns.Wrapped_ICalculator_83093f794c8d452e8af4524873a017de’。當調用此包裝類的某個方法時,CallHandlers將會被執行。

總結

Unity並不提供一個完整的AOP框架,因此使用它會有一些限制。但不管怎樣,使用Unity對象攔截功能來實現一些基本的AOP需求已經足夠了。

代碼下載

文章譯自

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM