本文翻譯自Spring.NET官方文檔Version 1.3.2。正則表達式
受限於我的知識水平,有些地方翻譯可能不許確,可是我仍是但願個人這些微薄的努力能爲他人提供幫助。spring
侵刪。性能優化
讓咱們看看 Spring.NET 如何處理通知。app
Spring.NET通知能夠被全部的被通知對象共享,或者針對不一樣的通知對象構造不一樣的通知。這個要看通知類型是per-class仍是per-instance。模塊化
per-class通知是最常常用到的通知類型,這種通知類型適用於泛型通知,例如事務通知器。它們不依賴於被代理對象的如今狀態或者一個其餘新的狀態,它們只關心方法和輸入參數。函數
per-instance通知類型適用於引入以支持混入(mixins)。在這種狀況下,通知會爲被代理對象添加狀態。性能
在AOP代理中也能夠混合使用共享通知和per-instance通知。優化
Spring.NET提供了多種能夠直接使用的通知類型,而且能夠支持多種其餘通知類型擴展。讓咱們看一些基礎的概念和標準的通知類型。spa
Spring.NET最基本的通知類型是環繞通知。.net
Spring.NET的環繞通知類型接口遵照AOP Alliance接口規範。環繞通知實現瞭如下接口:
1 public interface IMethodInterceptor : IInterceptor 2 { 3 object Invoke(IMethodInvocation invocation); 4 }
IMethodInvocation 傳到Invoke()的參數展現了須要被調用的方法;目標鏈接點;AOP代理和方法的其餘參數。Invoke()方法應該返回調用的結果:鏈接點的返回值。
一個簡單的IMethodInterceptor 實現以下:
1 public class DebugInterceptor : IMethodInterceptor { 2 public object Invoke(IMethodInvocation invocation) { 3 Console.WriteLine("Before: invocation=[{0}]", invocation); 4 object rval = invocation.Proceed(); 5 Console.WriteLine("Invocation returned"); 6 return rval; 7 } 8 }
須要注意的是MethodInvocation的Proceed()方法的調用。這個方法把攔截器鏈(interceptor chain )朝着鏈接點繼續下去。大多的攔截器都會調用這個方法,而後將這個方法的返回值返回。然而,一個IMethodInterceptor實例,就像其餘的環繞通知同樣,會返回一個不一樣的值或者拋出異常而不是調用Porceed()方法。但不管怎樣,你確定不會這麼作。
一個更加簡單的通知類型是前置通知。這種通知類型不須要實現IMethodInvocation接口,由於它只會在調用某個方法以前被調用。
前置通知的主要優勢在於它不須要調用Proceed()方法,所以不可能影響攔截而使整個攔截鏈斷裂。
IMethodBeforeAdvice 接口定義以下:
1 public interface IMethodBeforeAdvice : IBeforeAdvice 2 { 3 void Before(MethodInfo method, object[] args, object target); 4 }
須要主要的是,前置通知返回值類型是void。前置通知能夠在關鍵點執行以前插入自定義的行爲,可是不能改變返回值。若是一個前置通知拋出一個錯誤,它會忽視整個攔截鏈的其餘錯誤。錯誤將會經過攔截鏈傳遞上去。若是它是unchecked的或者是在被調用方法的簽名中,它會被直接傳遞給用戶,否則它就會被AOP代理包裝(wrapped)成unchecked錯誤。
如下是一個Spring.NET中的前置通知的例子,這個前置通知記錄了全部正常返回的方法的數量:
1 public class CountingBeforeAdvice : IMethodBeforeAdvice { 2 private int count; 3
4 public void Before(MethodInfo method, object[] args, object target) { 5 ++count; 6 } 7 public int Count { 8 get { return count; } 9 } 10 }
前置通知能夠應用在全部的切入點中。
異常後通知在關鍵點拋出異常以後被調用。Spring.Aop.IThrowsAdvice接口不包含任何方法:它是一個標籤接口(tag interface )代表實體實現一個或者多個異常後通知方法。這些異常後通知方法必須是這樣組成:
1 AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass
異常後通知方法必須被叫作「'AfterThrowing'」。它的返回值會被Spring.NET AOP framework忽視,因此返回值通常是void。關於方法的傳入參數,只有最後一個參數是必要的。所以,這個方法只有一個或者四個傳入參數,這要根據通知方法是否對方法,方法參數和目標對象這幾個因素感興趣。
下面的方法片斷展現了一些異常後通知的例子:
這個方法會被會在RemotingException 異常拋出後被調用(包括子類):
1 public class RemoteThrowsAdvice : IThrowsAdvice { 2 public void AfterThrowing(RemotingException ex) { 3 // Do something with remoting exception
4 } 5 }
下面的通知會在SqlException 異常拋出後背調用。和上面的通知不同,它有4個傳入參數,因此它要得到被通知方法,方法參數和目標實體等信息:
1 public class SqlExceptionThrowsAdviceWithArguments : IThrowsAdvice { 2 public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) { 3 // Do something will all arguments
4 } 5 }
最後的這個例子展現了這兩個方法如何在一個類中實現,他們處理了RemotingException 和SqlException兩個異常。任意數量的異常後通知均可以在一個類中實現,就像下面的例子同樣:
1 public class CombinedThrowsAdvice : IThrowsAdvice { 2 public void AfterThrowing(RemotingException ex) { 3 // Do something with remoting exception
4 } 5 public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) { 6 // Do something will all arguments
7 } 8 }
最後,有必要提一下,異常後通知僅僅應用在實際上拋出的異常。這個是什麼意思呢?這個意思就是若是你在以前已經定義了處理RemotingException的通知,可調用方法'AfterThrowing'將僅在拋出異常是RemotingException 的時候被調用。若是RemotingException異常被拋出,而這個異常被包裹在其餘異常中間而且在其餘異常以前拋出,這時處理RemotingException 的異常通知就不會被調用。考慮一個處理RemotingException異常後通知的業務方法,若是在方法調用期間這個業務方法拋出一個RemotingException ,而RemotingException 是被包裹在一個業務裏特定的BadConnectionException 之中(能夠看如下的代碼片斷),這時異常後通知就不會響應RemotingException由於異常後通知只監測到BadConnectionException。事實上,被包裹在BadConnectionException 中的RemotingException 是不重要的。
1 public void BusinessMethod() 2 { 3 try
4 { 5 // do some business operation...
6 } 7 catch (RemotingException ex) 8 { 9 throw new BadConnectionException("Couldn't connect.", ex); 10 } 11
Spring.NET中的返回後通知必須實現Spring.Aop.IAfterReturningAdvice接口,以下:
1 public interface IAfterReturningAdvice : IAdvice 2 { 3 void AfterReturning(object returnValue, MethodBase method, object[] args, object target); 4 }
返回後通知能夠得到返回值(可是不能改變它),調用方法,方法的參數和目標。
下面的返回後通知記錄了全部被成功調用,而且沒有拋出任何異常的方法數量:
1 public class CountingAfterReturningAdvice : IAfterReturningAdvice { 2 private int count; 3 public void AfterReturning(object returnValue, MethodBase m, object[] args, object target) { 4 ++count; 5 } 6 public int Count { 7 get { return count; } 8 } 9 }
這個通知沒有改變執行過程。若是它拋出了異常,攔截鏈會拋出異常而不是返回結果。
當多個通知存在於同一個鏈接點上,優先順序由通知實現的IOrdered接口決定或者在通知器中指定。
Spring.NET 容許你向一個被通知的類添加新的方法和屬性。當你想添加的功能是一個橫切關注點,而且你想把這個功能引入到這個類既定的繼承結構中。例如你可能想在你代碼中把實體注入到一個接口中。引入也是多重繼承的一種方法。
引入就是就是經過使用一個通常的實現IAdvice接口聲明方式實現IAdvice。
做爲一個簡單的例子,考慮一個IAuditable接口,這個接口功能是記錄一個實體最後修改的時間:
1 public interface IAuditable : IAdvice 2 { 3 DateTime LastModifiedDate 4 { 5 get; 6 set; 7 } 8 }
IAdvice接口細節以下:
1 public interface IAdvice 2 { 3 }
能夠經過實現ITargetAware來得到被通知的實體:
1 public interface ITargetAware 2 { 3 IAopProxy TargetProxy 4 { 5 set; 6 } 7 }
IAopProxy包含了一個間接得到被通知實體的引用功能:
1 public interface IAopProxy 2 { 3 object GetProxy(); 4 }
一個簡單的描述這個功能的類以下定義:
1 public interface IAuditable : IAdvice, ITargetAware 2 { 3 DateTime LastModifiedDate 4 { 5 get; 6 set; 7 } 8 }
一個實現這個接口的類定義以下:
1 public class AuditableMixin : IAuditable 2 { 3 private DateTime date; 4 private IAopProxy targetProxy; 5 public AuditableMixin() 6 { 7 date = new DateTime(); 8 } 9 public DateTime LastModifiedDate 10 { 11 get { return date; } 12 set { date = value; } 13 } 14 public IAopProxy TargetProxy 15 { 16 set { targetProxy = value; } 17 } 18 }
引入通知並不和一個切入點關聯,由於它是應用在類層面而不是模型層面。所以,引入通知使用他們本身的 IAdvisor接口子類,名叫 IIntroductionAdvisor,來定義這個引入通知能夠應用的類型。
1 public interface IIntroductionAdvisor : IAdvisor 2 { 3 ITypeFilter TypeFilter { get; } 4
5 Type[] Interfaces { get; } 6 void ValidateInterfaces(); 7 }
TypeFilter屬性返回了這個引入通知應用的對象類型的過濾結果。
Interfaces屬性返回被通知器引入的接口。
ValidateInterfaces()方法在內部用來查看被引入的接口是否是可以被引入通知實現。
Spring.NET提供了一個默認的接口實現(DefaultIntroductionAdvisor類),足夠應付你須要使用引入通知的大多數場景。一個最簡單的引入通知器的實現方式是繼承DefaultIntroductionAdvisor,而後再構造函數中傳入一個新實例。傳入一個新實例十分重要,由於咱們想要爲每一個被通知的實例都能使用這個混合類(mixin class)。
1 public class AuditableAdvisor : DefaultIntroductionAdvisor 2 { 3 public AuditableAdvisor() : base(new AuditableMixin()) 4 { 5 } 6 }
你也能夠顯示指定要引入的接口。能夠經過查看SDK文檔以得到更多信息。
要生成引入通知器,咱們可使用IAdvised.AddIntroduction()方法或者在XML配置文檔中在ProxyFactoryObject下使用IntroductionNames屬性節點,稍後會介紹這個方法。
和Java中Spring Framework的AOP實現不一樣,Spring.NET 的引入通知並非一種特別的攔截器類型。這種方法的優勢在於引入通知不是在攔截鏈中,這就能夠進行一些性能優化。當一個方法不經過攔截器被調用的時候,咱們就能夠直接調用方法而不是都使用反射機制,不管這個方法是在目標對象中仍是引入的一部分。也就是說引入的方法和實體的方法性能相同,這在向一個細粒度的實體引入方法的時候頗有用。缺點在於若是這個混合的功能沒法再在調用堆棧中訪問到。這個問題會在未來的Spring.NET AOP中解決。
在Spring.NET中,通知器是切面的模塊化。通知器通常由一個通知和一個切入點組合構成。
除了引入通知以外,全部的通知器均可以和任何通知使用。Spring.Aop.Support.DefaultPointcutAdvisor類是一個最經常使用的通知器實現,能夠和IMethodInterceptor, IBeforeAdvice 或者 IThrowsAdvice接口以及其餘任意的切入點定義共同使用。
其餘便捷的實現方式包括:在13.2.3.1.2節介紹的AttributeMatchMethodPointcutAdvisor ,基於特性的切入點。RegularExpressionMethodPointcutAdvisor ,基於正則表達式的切入點。
在Spring.NET中,咱們能夠在一個AOP代理中混入多種通知器和通知類型。例如,你能夠在一個代理配置中使用環繞通知,拋出異常後通知和前置通知:Spring.NET 會自動生成須要的攔截鏈。