Spring.NET教程(十四)AOP的通知類型(基礎篇)
上篇我們學習了AOP的基本概念,我們回顧一下上篇提到的Advice(通知):所謂通知是指攔截到joinpoint(連接點)之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,環繞通知。
Spring.net的通知既可由某個類的所有對象共享,也可由該類型的單個實例獨占。共享的通知稱為基於類型(per-class)的通知,而獨占的通知稱為基於實例(per-instance)的通知。
基於類型的通知最為常用。很多常用功能很適合用基於類型的通知實現,比如說事務。它們不依賴於目標對象的狀態,也不會向目標對象添加新狀態,僅僅對方法及其參數進行操作。
基於實例的通知比較適合做引入(introductions)。此時通知可以向目標對象添加狀態。在AOP代理中,可以同時使用基於類型和基於實例的通知。Spring.NET和spring框架略有不同,只有四種通知類型,沒有spring框架的最終通知(目前我還沒有實現最終通知,如果有朋友實現的話,可以給我留言)。
一、攔截環繞通知(around advice):Spring.NET中最基本的通知類型是攔截環繞通知(interception around advice),即方法攔截器。攔截環繞通知繼承IMethodInterceptor接口。註意其中IMethodInvocation.Proceed()方法的調用。該方法會依次調用攔截器鏈上的其它攔截器。大部分攔截器都需要調用這個方法並返回它的返回值。當然,也可以不調用Proceed方法,而返回一個其它值或拋出一個異常,但一般不太會這麽做。
二、前置通知(before advise):是在IMethodInterceptor.Proceed()方法調用前的通知。繼承自IMethodBeforeAdvice接口。
三、異常通知(throws advise):是在IMethodInterceptor.Proceed()方法調用時發生異常的通知。繼承自IthrowsAdvice接口。IthrowsAdvice接口沒有定義任何方法:它是一個標識接口(按:之所以用標識接口,原因有二:1、在通知方法中,只有最後一個參數是必須的。如果聲明為接口的方法,參數列表就被固定了。2、如果第一個原因可以用重載的接口方法解決,那麽這個原因就是使用標識接口的充分原因了:實現此接口的類必須聲明一或多個通知方法,接口方法做不到這一點),用以表明實現它的類聲明了一或多個強類型的異常通知方法。
四、後置通知(after returning advise):是在IMethodInterceptor.Proceed()方法調用後的通知。繼承自IAfterReturningAdvice接口。後置通知對切入點的執行沒有影響,如果通知拋出異常,就會沿攔截器鏈向上拋出,從而中斷攔截器鏈的繼續執行。
代碼實現:
準備條件
四種通知:
AroundAdvise
/**//// <summary>
/// 環繞通知
/// </summary>
public class AroundAdvise : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
Console.Out.WriteLine(string.Format(" 環繞通知: 調用的方法 ‘{0}‘", invocation.Method.Name));
Console.WriteLine();
object returnValue = null;
try
{
returnValue = invocation.Proceed();
}
catch
{
Console.Out.WriteLine(" 環繞通知: 發生異常");
Console.WriteLine();
}
Console.Out.WriteLine(String.Format(" 環繞通知: 返回值 ‘{0}‘", returnValue));
return returnValue;
}
}
BeforeAdvise
/**//// <summary>
/// 前置通知
/// </summary>
public class BeforeAdvise : IMethodBeforeAdvice
{
public void Before(MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine(" 前置通知: 調用的方法名 : " + method.Name);
Console.Out.WriteLine(" 前置通知: 目標 : " + target);
Console.Out.WriteLine(" 前置通知: 參數為 : ");
if (args != null)
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
Console.WriteLine();
}
}
ThrowsAdvise
/**//// <summary>
/// 異常通知
/// </summary>
public class ThrowsAdvise : IThrowsAdvice
{
public void AfterThrowing(Exception ex)
{
string errorMsg = string.Format(" 異常通知: 方法拋出的異常 : {0}", ex.Message);
Console.Error.WriteLine(errorMsg);
Console.WriteLine();
}
}
AfterReturningAdvise
/**//// <summary>
/// 後置通知
/// </summary>
public class AfterReturningAdvise : IAfterReturningAdvice
{
public void AfterReturning(object returnValue, MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine(" 後置通知: 方法調用成功,方法名 : " + method.Name);
Console.Out.WriteLine(" 後置通知: 目標為: " + target);
Console.Out.WriteLine(" 後置通知: 參數 : ");
if (args != null)
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
Console.Out.WriteLine(" 後置通知:返回值是 : " + returnValue);
Console.WriteLine();
}
}
接口:
public interface IOrderService
{
object Save(object id);
}
一、沒有異常的情況
OrderService
public class OrderService : IOrderService
{
/**//// <summary>
/// 攔截該方法
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public object Save(object id)
{
//throw new Exception("由於XXX原因保存出錯");
return "保存:" + id.ToString();
}
}
Program
class Program
{
static void Main(string[] args)
{
ProxyFactory factory = new ProxyFactory(new OrderService());
factory.AddAdvice(new AroundAdvise());
factory.AddAdvice(new BeforeAdvise());
factory.AddAdvice(new AfterReturningAdvise());
factory.AddAdvice(new ThrowsAdvise());
IOrderService service = (IOrderService)factory.GetProxy();
object result = service.Save(1);
Console.WriteLine();
Console.WriteLine(string.Format("客戶端返回值:{0}", result));
Console.ReadLine();
}
}
輸出效果:見圖1
圖1
二、有異常的情況:
OrderService
public class OrderService : IOrderService
{
/**//// <summary>
/// 攔截該方法
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public object Save(object id)
{
throw new Exception("由於XXX原因保存出錯");
//return "保存:" + id.ToString();
}
}
輸出效果:見圖2
圖2
從圖與代碼中,我們不難看出Advice(通知)的生命周期。攔截環繞通知(around advice)圍繞著整個攔截過程;前置通知(before advise)在方法調用前執行;異常通知(throws advise)在調用方法時發生異常才執行,否則不執行;後置通知(after returning advise)在方法調用後執行,當調用時出現異常,則不執行後置通知(after returning advise)。
Spring.NET教程(十四)AOP的通知類型(基礎篇)