浅析Spring.net 中的Aop使用
一. 什么是AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加 功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例 如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并 不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
切面(Aspect)
切面是你要实现的交叉功能。它是应用系统模块化的一个切面或领域。切面的最常见(虽然简单)例子是日志记录。日志记录在系统中到处需要用到,利用继承来重用日志模块不适合。然而,你可以创建一个日志记录切面,并且使用AOP在系统中应用。
连接点(Joinpoint)
连接点是应用程序执行过程中插入切面的地点。这个地点可以是方法调用,异常抛出,或者甚至是要修改的字段。切面代码在这些地方插入到你的应用流程中,添加新的行为。
通知(Advice)
通知切面的实际实现。它通知应用系统新的行为。在日志例子中,日志通知包含了实现实际日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。
切入点(Pointcut)
切入点定义了通知应该应用在哪些连接点。通知可以应用到AOP框架支持的任何连接点。当然,你并不希望把所有切面应用到所有可能的连接点上。切入点让你指定通知应用到什么地方。通常通过指定类名和方法名,或者匹配类名和方法名式样的正则表达式来指定切入点。一些AOP框架允许动态创建切入点,在运行时根据条件决定是否应用切面,如方法参数值。
引入(Introduction)
引入允许你为已存在类添加新方法和属性。例如,你可以创建一个稽查通知来记录对象的最后修改时间。只要用一个方法setLastMofified(Date)以及一个保存这个状态的变量。可以在不改变已存在类的情况下将这个引入,给他们新的行为和状态。
目标对象(Target)
目标对象是被通知对象。它既可以是你编写的类也可以是你要添加制定行为的第三方类。如果没有AOP,这个类就必须要包含它的主要逻辑以及其他交叉业务逻辑。有了AOP,目标对象就可以全身心地关注主要业务,忘记应用其上的通知。
代理(Proxy)
代理是将通知应用到目标对象后创建的对象。对于客户对象来说,目标对象(应用AOP之前的对象)和代理对象(应用AOP之后的对象)是一样的。也就是,应用系统的其他部分不用为了支持代理对象而改变。
织入 (Weaving)
织入是将切面应用到目标对象从而创建一个新的代理对象的过程。切面在指定接入点被织入到目标对象中。
二. 几个重要接口
用于找到用于通知的相关的类型和方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface IPointcut 2 3 { 4 5 ITypeFilter TypeFilter { get; } 6 7 IMethodMatcher MethodMatcher { get; } 8 9 }
用于匹配相关类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface ITypeFilter 2 3 { 4 5 bool Matches(Type type); 6 7 } 8 9 public interface IMethodMatcher 10 11 { 12 13 bool IsRuntime { get; } 14 15 bool Matches(MethodInfo method, Type targetType); 16 17 bool Matches(MethodInfo method, Type targetType, object[] args); 18 19 }
Matches(MethodInfo, Type)方法用来测试这个切入点是否匹配目标类的指定方法。这将在AOP代理被创建的时候执行,这样可以避免在每次方法调用的时候都执行。如果两个参数的matches方法对于一个给定的方法返回true,并且IMethodMatcher接口的IsRuntime方法也返回true,那么有三个参数的matches方法将在每个方法调用时被调用. 这使得切入点在通知将被执行前可以查看传入到方法的参数。
绝大多数的IMethodMatcher接口是static的,这也就意味着它们的IsRuntime属性返回false.在这种情况下,有三个参数的Matches方法将永远不会被调用.
三. 通知
3.1 通知生命周期
3.2 通知类型
一、拦截环绕通知(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接口。后置通知对切入点的执行没有影响,如果通知抛出异常,就会沿拦截器链向上抛出,从而中断拦截器链的继续执行。
3.2.1 Interception Around Advice(环绕拦截通知,后面的讲解以环绕拦截通知为例子)
方法拦截器接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface IMethodInterceptor : IInterceptor 2 3 { 4 5 object Invoke(IMethodInvocation invocation); 6 7 }
模拟环绕拦截通知
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class DebugInterceptor : IMethodInterceptor 2 { 3 4 public object Invoke(IMethodInvocation invocation) 5 { 6 7 Console.WriteLine("Before: invocation=[{0}]", invocation); 8 9 object rval = invocation.Proceed(); 10 11 Console.WriteLine("Invocation returned"); 12 13 return rval; 14 15 } 16 17 } 18 19
注意 IMethodInvocation 的 Proceed 方法,proceed方法返回方法的返回值.
四. 切入点操作
4.1 静态切入点
4.1.1 ProxyFactoryObject 显式创建AOP代理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <object id="UserValidateTarget" type="Stephen.SpringNet.AOPSample.Servcies.Impl.UserValidate, Servcies"></object> 2 3 <object id="RoundInterceptor" type="Stephen.SpringNet.AOPSample.Servcies.Interceptor.RoundInterceptor, Servcies"></object> 4 5 <object id="UserValidateProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> 6 7 <property name="proxyInterfaces" value="Stephen.SpringNet.AOPSample.Servcies.IValidateService"/> 8 9 <property name="target" ref="UserValidateTarget"/> 10 11 <property name="interceptorNames"> 12 13 <list> 14 15 <value>RoundInterceptor</value> 16 17 </list> 18 19 </property> 20 21 </object>
不过由于显式的创建AOP在要创建多个代理的时候需要重复的配置因此Spring提供了自动代理
4.1.2 ObjectNameAutoProxyCreator 对象名称自动切入点
可以用特定的文本值或通配符匹配目标对象的名称,并为满足条件的目标对象创建AOP代理。该类支持模式匹配字符串,如:"*name","name*",”*name*“和精确文本如"name"。我们可以通过下面这个简单的例子了解一下自动代理的功能。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <object id="IValidateProxy" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> 2 3 <property name="ObjectNames"> 4 5 <list> 6 7 <value>*Validate</value> 8 9 </list> 10 11 </property> 12 13 <property name="InterceptorNames" value="RoundInterceptor"></property> 14 15 </object>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 [TestMethod] 2 3 public void ObjectNameAutoProxyCreatorMethodTest() 4 5 { 6 7 var context = ContextRegistry.GetContext(); 8 9 IDictionary validates = context.GetObjectsOfType(typeof(IValidateService)); 10 11 foreach (DictionaryEntry validate in validates) 12 13 { 14 15 ((IValidateService) validate.Value).Validate(null); 16 17 } 18 19 }
执行结果
ObjectNameAutoProxyCreatorMethodTest : Passed
Method:Validate开始执行
Method:Validate执行完毕
4.1.3 SdkRegularExpressionMethodPointcut 通过正则表达式来匹配需要执行的类或方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <object id="ValidatePointCut" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> 2 <property name="Pattern" value="UserValidate.Advance*"></property> 3 </object> 4 <aop:config> 5 <aop:advisor advice-ref="RoundInterceptor" pointcut-ref="ValidatePointCut"/> 6 </aop:config>
pattern 的属性 UserValidate.Advance* 表示 匹配 UserValidate 类中的以Advance开头的方法
4.1.4 DefaultAdvisorAutoProxyCreator+RegularExpressionMethodPointcutAdvisor 创建正则表达式AOP
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/> <object id="ValidateRegularExpressionPointCut" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"> 2 <property name="advice" ref="RoundInterceptor"/> 3 <property name="patterns"> 4 <list> 5 <value>UserValidate.Advance*</value> 6 </list> 7 </property> 8 </object>
4.1.5 特性Aop
可以通过Attribute类来实现AOP
[AttributeUsage(AttributeTargets.Method)] public class AopAttribute:Attribute { }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <object id="aroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop"> 2 3 <property name="Advice" ref="RoundInterceptor"/> 4 5 <property name="Attribute" 6 7 value ="Stephen.SpringNet.AOPSample.Servcies.AopAttribute, Servcies" /> 8 9 </object> 10 11 12 13 <object id="ValidateAttributeProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> 14 15 <property name="proxyInterfaces" value="Stephen.SpringNet.AOPSample.Servcies.IValidateService"/> 16 17 <property name="target" ref="UserValidateTarget"/> 18 19 <property name="interceptorNames"> 20 21 <list> 22 23 <value>aroundAdvisor</value> 24 25 </list> 26 27 </property> 28 29 </object>