本文翻译自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 会自动生成须要的拦截链。