Spring.NET AOP技术学习

什么是Spring.NET AOP?下面我们先来了解Spring.NET AOP技术的关键概念:

方面(Aspect):对横向分布在多个对象中的关注点所做的模块化。在企业应用中,事务管理就是一个典型的横切关注点。 Spring.NET将 方面实现为Advisor或拦截器(interceptor)。(按:Advisor是通知和切入点的组合,拦截器实际就是指通知,注意在本文档中,一般 会把环绕通知称为拦截器,而将其它类型的通知称为通知,这是因为环绕通知实现的是 AopAlliance.Intercept.IMethodInterceptor接口,而其它通知类型实现的都是Spring.Aop命名空间下的通 知接口。)

连接点(Joinpoint):程序执行过程中的一个点,例如对某个方法的调用或者某个特定异常的抛出都可以称为连接 点。

通知(Advice):AOP框架在某个连接点所采取的行为。通知有多种类型,包括“环绕”通知,“前置”通知和“异 常”通知等,后文将对通知类 型进行讨论。包括Spring.NET在内的很多AOP框架都把通知建模为拦截器(interceptor),并且会维护一个"包围"在连接点周围的拦截 器链。

切入点(Pointcut):指通知的应用条件,用于确定某个通知要被应用到哪些连接点上。AOP框架应允许让开发人 员指定切入点,例如,可以使用正则表达式来指定一个切入点。

引入(Introduction):向目标对象添加方法或字段的行为。Spring.NET允许为任何目标对象引入新 的接口。例如,可以利用引入让任何对象在运行期实现IAuditable接口,以简化对象状态变化的跟踪过程。(按:也称为mixin,混入)

目标对象(Target object):指包含连接点的对象。也称为被通知或被代理对象。(按:“被通知对象”实际 是“被应用了通知的对象”,在译文中,将advised object或proxied object统称为目标对象,这样更为统一)

AOP代理(AOP proxy):由AOP框架在将通知应用于目标对象后创建的对象。在Spring.NET 中,AOP代理是使用IL代码在运行时创建的动态代理。

织入(Weaving):将方面进行组装,以创建一个目标对象。织入可以在编译期完成(例如使用 Gripper_Loom.NET编译器),也可以在运行时完成。Spring.NET在运行时执行织入。



各种通知类型包括:

环绕通知(Around Advise):包 围(按:即在连接点执行的前、后执行)某个连接点(如方法调用)的通知。这是功能最强大的一种通知。环绕通知允许在方法调用的前后执行自定义行为。它可以 决定是让连接点继续执行,还是用自己的返回值或异常来将连接点“短路”。

前置通知(Before Advise):在某个连接点执行之前执行,但是不具备阻止连接点继续执行的能力(除非它抛出异常)。

异常通知(Throws Advise):当 方法(连接点)抛出异常时执行。Spring.NET的异常通知是强类型的(按:Spring.NET用标识接口来定义异常通知,异常 通知的处理方法仅需遵循一定的命名规则,可以用具体的异常类型声明其参数,参见12.3.2.3节),所以,可以在代码中直接捕捉某个类型的异常(及其子 类异常),不必从Exception转型。

后置通知(After returning Advise):在连接点正常执行完成后执行,例如,如果方法正常返回,没有抛出异常时,后置通知就会被执行。

Spring.NET内置了以上所有类型的通知。在应用时,应尽量使用功能最少(只要对要实现的行为来说是足够的)的通知类型,这样可简化编程模 型并减少出错的可能。例如,如果只需使用某个方法的返回值来更新缓存,那么用后置通知就比环绕通知合适。因为,尽管环绕通知可以完成同样的功能,但在后置 通知中不用象环绕通知那样必须调用IMethodInvocation接口的Proceed()方法来允许连接点继续执行,所以连接点总是能正常执 行。(按:换句话说,因为环绕通知可以控制连接点的继续执行,所以如果没有调用IMethodInvocation接口的Proceed()方法,连接点 就会被“短路”;而使用后置通知就不存在这个问题)。

切入点是AOP的关键概念,使AOP从根本上区别于旧的拦截技术。切入点使通知可以独立于OO的继承层次之外。例如,一个声明式事务处理的环绕通知可以应 用于不同对象的方法。切入点是AOP的结构性要素。

代 码实例:

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 )。


posted @ 2011-05-23 09:07  铁芒阁  阅读(544)  评论(0编辑  收藏  举报
分享到: