一
什么是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)
织入是将切面应用到目标对象从而创建一个新的代理对象的过程切面在指定接入点被织入到目标对象中
二 几个重要接口
用于找到用于通知的相关的类型和方法
View Code
public interface IPointcut
{
ITypeFilter TypeFilter { get; }
IMethodMatcher MethodMatcher { get; }
}
用于匹配相关类型
View Code
public interface ITypeFilter
{
bool Matches(Type type)
}
public interface IMethodMatcher
{
bool IsRuntime { get; }
bool Matches(MethodInfo method Type targetType)
bool Matches(MethodInfo method Type targetType object[] args)
} Matches(MethodInfo Type)
方法用来测试这个切入点是否匹配目标类的指定方法这将在AOP代理被创建的时候执行这样可以避免在每次方法调用的时候都执行如果两个参数的matches方法对于一个给定的方法返回true并且IMethodMatcher接口的IsRuntime方法也返回true那么有三个参数的matches方法将在每个方法调用时被调用 这使得切入点在通知将被执行前可以查看传入到方法的参数
绝大多数的IMethodMatcher接口是static的这也就意味着它们的IsRuntime属性返回false在这种情况下有三个参数的Matches方法将永远不会被调用
三 通知
通知生命周期
通知类型
一拦截环绕通知(around advice)SpringNET中最基本的通知类型是拦截环绕通知(interception around advice)即方法拦截器拦截环绕通知继承IMethodInterceptor接口注意其中IMethodInvocationProceed()方法的调用该方法会依次调用拦截器链上的其它拦截器大部分拦截器都需要调用这个方法并返回它的返回值当然也可以不调用Proceed方法而返回一个其它值或抛出一个异常但一般不太会这么做
二前置通知(before advise)是在IMethodInterceptorProceed()方法调用前的通知继承自IMethodBeforeAdvice接口
三异常通知(throws advise)是在IMethodInterceptorProceed()方法调用时发生异常的通知继承自IthrowsAdvice接口IthrowsAdvice接口没有定义任何方法它是一个标识接口(按之所以用标识接口原因有二在通知方法中只有最后一个参数是必须的如果声明为接口的方法参数列表就被固定了如果第一个原因可以用重载的接口方法解决那么这个原因就是使用标识接口的充分原因了实现此接口的类必须声明一或多个通知方法接口方法做不到这一点)用以表明实现它的类声明了一或多个强类型的异常通知方法
四后置通知(after returning advise)是在IMethodInterceptorProceed()方法调用后的通知继承自IAfterReturningAdvice接口后置通知对切入点的执行没有影响如果通知抛出异常就会沿拦截器链向上抛出从而中断拦截器链的继续执行
Interception Around Advice(环绕拦截通知后面的讲解以环绕拦截通知为例子)
方法拦截器接口
View Code
public interface IMethodInterceptor : IInterceptor
{
object Invoke(IMethodInvocation invocation)
}
模拟环绕拦截通知
View Code
public class DebugInterceptor : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
ConsoleWriteLine(Before: invocation=[{}] invocation)
object rval = invocationProceed()
ConsoleWriteLine(Invocation returned)
return rval;
}
}
注意 IMethodInvocation 的 Proceed 方法proceed方法返回方法的返回值
四 切入点操作
静态切入点
ProxyFactoryObject 显式创建AOP代理
AOP 配置文件
<object id=UserValidateTarget type=StephenSpringNetAOPSampleServciesImplUserValidate Servcies></object>
<object id=RoundInterceptor type=StephenSpringNetAOPSampleServciesInterceptorRoundInterceptor Servcies></object>
<object id=UserValidateProxy type=SpringAopFrameworkProxyFactoryObject SpringAop>
<property name=proxyInterfaces value=StephenSpringNetAOPSampleServciesIValidateService/>
<property name=target ref=UserValidateTarget/>
<property name=interceptorNames>
<list>
<value>RoundInterceptor</value>
</list>
</property>
</object>
不过由于显式的创建AOP在要创建多个代理的时候需要重复的配置因此Spring提供了自动代理
ObjectNameAutoProxyCreator 对象名称自动切入点
可以用特定的文本值或通配符匹配目标对象的名称并为满足条件的目标对象创建AOP代理该类支持模式匹配字符串如*namename**name*和精确文本如name我们可以通过下面这个简单的例子了解一下自动代理的功能
AOP 配置文件
<object id=IValidateProxy type=SpringAopFrameworkAutoProxyObjectNameAutoProxyCreator SpringAop>
<property name=ObjectNames>
<list>
<value>*Validate</value>
</list>
</property>
<property name=InterceptorNames value=RoundInterceptor></property>
</object> 测试方法
[TestMethod]
public void ObjectNameAutoProxyCreatorMethodTest()
{
var context = ContextRegistryGetContext()
IDictionary validates = contextGetObjectsOfType(typeof(IValidateService))
foreach (DictionaryEntry validate in validates)
{
((IValidateService) validateValue)Validate(null)
}
}
执行结果
ObjectNameAutoProxyCreatorMethodTest : Passed
Method:Validate开始执行
Method:Validate执行完毕
SdkRegularExpressionMethodPointcut 通过正则表达式来匹配需要执行的类或方法
AOP 配置
<object id=ValidatePointCut type=SpringAopSupportSdkRegularExpressionMethodPointcut SpringAop>
<property name=Pattern value=UserValidateAdvance*></property>
</object>
<aop:config>
<aop:advisor adviceref=RoundInterceptor pointcutref=ValidatePointCut/>
</aop:config> pattern 的属性 UserValidateAdvance* 表示 匹配 UserValidate 类中的以Advance开头的方法
DefaultAdvisorAutoProxyCreator+RegularExpressionMethodPointcutAdvisor 创建正则表达式AOP
AOP 配置
<object id=ProxyCreator type=SpringAopFrameworkAutoProxyDefaultAdvisorAutoProxyCreator SpringAop/>
<object id=ValidateRegularExpressionPointCut type=SpringAopSupportRegularExpressionMethodPointcutAdvisor SpringAop>
<property name=advice ref=RoundInterceptor/>
<property name=patterns>
<list>
<value>UserValidateAdvance*</value>
</list>
</property>
</object>
特性Aop
可以通过Attribute类来实现AOP
[AttributeUsage(AttributeTargetsMethod)]
public class AopAttribute:Attribute
{
} AOP 配置
<object id=aroundAdvisor type=SpringAopSupportAttributeMatchMethodPointcutAdvisor SpringAop>
<property name=Advice ref=RoundInterceptor/>
<property name=Attribute
value =StephenSpringNetAOPSampleServciesAopAttribute Servcies />
</object>
<object id=ValidateAttributeProxy type=SpringAopFrameworkProxyFactoryObject SpringAop>
<property name=proxyInterfaces value=StephenSpringNetAOPSampleServciesIValidateService/>
<property name=target ref=UserValidateTarget/>
<property name=interceptorNames>
<list>
<value>aroundAdvisor</value>
</list>
</property>
</object>