1. 程式人生 > >Spring.NET教程(十四)AOP的通知類型(基礎篇)

Spring.NET教程(十四)AOP的通知類型(基礎篇)

() main 框架 所有 intro each 多個 如果 基本

上篇我們學習了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的通知類型(基礎篇)