Spring AOP采用Java作为AOP的实现语言(AOL),学习曲线平滑,Spring AOP对AspectJ也提供了很好的集成与支持。最为第二代的AOP,采用动态代理机制和字节码生产技术实现,在运行期间为目标对象生成一个代理对象,将横切逻辑织入到这个代理对象中,系统使用织入了横切逻辑的代理对象。
在深入剖析Spring AOP之前,我们有必要先了解一下代理模式,代理模式可以简单地分成:静态代理模式和动态代理模式。静态代理模式作为设计模式中比较简单的一种模式,我们不做过多的分析;动态代理模式是JDK1.3之后引入的,使用该机制,我们可以为指定的接口在系统运行期间动态地生成代理对象。动态代理机制与静态代理的区别就在于把原先的一个代理类划分成一个Proxy类再加一个InvocationHandler接口,Proxy负责生产相应的动态代理实例,InvocationHandler负责拦截相应的方法调用。InvocationHandler就是实现横切逻辑的地方,作用类似于Adcice。动态代理模式的缺陷是只对实现了Interface的类有效。Spring AOP默认使用动态代理机制,无效时采用名为CGLIB的动态字节码生产类库,为目标对象生成动态的代理对象实例。
我们知道,基于面向对象语言的继承性,子类对象可以作为父类来使用。但是,使用继承的方式具有和静态代理一样的问题,需要为每个不同类型的目标对象都单独创建相应的扩展子类,借助CGLIB这样的动态字节库,在系统运行期间动态地为目标生成相应的扩展子类。使用CGLIB的唯一限制是无法对final类和final方法进行复写。
Spring AOP只支持方法执行类型的Joinpoint,这样做是为了付出20%的努力达到80%的效果。Spring AOP中以接口定义Pointcut作为其AOP框架中所有Pointcut的最顶层抽象,其中定义了两个方法用来帮助扑捉相应的Jointpoint。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
对于其中的ClassFilter和MethodMatcher,我们看一下他们的具体接口定义:
public interface ClassFilter {
boolean matches(Class clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
public interface MethodMatcher {
boolean matches(Method method, Class targetClass);
boolean isRuntime();
boolean matches(Method method, Class targetClass, Object[] args);
MethodMather TRUE = TrueMethodMatcher.INSTANCE;
}
简单来说,ClassFilter的存在是为了进行类的过滤,粒度比较粗,MethodMatcher是做方法的过滤,而且为了提高性能重载了matches方法,分为静态过滤和动态过滤,它们之间的分界线是isRuntime方法。
在Spring AOP中,已经为我们提供了好几个Pointcut实现,满足我们一般的需求。我们来简单分析一下各种实现的特点。
+ NameMatchMethodPointcut 属于静态匹配的子类,只考虑方法名,不考虑参数
+ AbstractRegexMethodPointcut 利用正则表达式进行匹配,可以匹配整个方法签名
+ AnnotationMatchingPointcut 利用注解来进行匹配,分为类级别和方法级别
+ ComposablePointcut 可进行逻辑运行的Pointcut实现
+ ControlFlowPointcut 匹配程序的调用流程
除了以上介绍的各种Pointcut,如果有自己的需求,我们也可以实现自己的Pointcut。
Spring AOP加入了开源组织,Spring中Advice的实现全部遵循AOP Alliance规定的接口。我们知道Advice是承载横切逻辑的地方,根据自身实例能否被目标对象类的所有实例所共享,划分成per-class类型与per-instance类型。只有Introduction类型的Advice属于per-instance类型,不能共享。
下面,我们开始简单地介绍各种Advice:
+ Before Advice 可通过抛出异常来打断方法流程
+ ThrowsAdvice 可用来进行异常情况的监控
+ AfterReturningAdvice 可访问当前Joinpoint的返回值、方法、方法参数和目标对象,但是不能更改返回值。
+ Around Advice 就是MethodInterceptor接口,功能非常强大。
以上介绍的都是pre-class类型的Advice。下面我们开始介绍Introduction,该Advice可以在不改动目标类定义的情况下,为目标类添加新的属性以及行为。根据是否可以在运行时动态配置和判断当前Introduction可应用的目标接口类型,划分为静态分支和动态分支。
在知道Pointcut和Advice之后,我们再看把他们集合到一起的Aspect。AOP中Aspect可以包含多个Pointcut和多个Advice,而我们Spring AOP中的对等实体Advisor是一种特殊的Aspect,通常只持有一个Pointcut和一个Advice。关于Advisor的体系结构,我们可以简单地分成两个分支:
针对其中PointcutAdvisor,我们来看一下其常见的子类:
+ DefaultPointcutAdvisor 除了Intorduction以外的Advice都可以
+ NamematchMethodPointcutAdvisor 限制只能使用NameMatchMethodPointcut
+ RegexMethodPointcutAdvisor 限制只能使用AbstractRegexMethodPointcut
+ DefalutBeanfactoryPointcutAdvisor 绑定到了BeanFactory,使用较少
对于IntroductionAdvisor,其只能用于类级别的拦截,只能够使用Introduction型的Advice,其常见子类只有一个:
+ DefaultIntroductionAdvisor
为了解决同一个Joinpoint应用的多个Advice执行的先后顺序问题,我们可以指定它们的Order值,默认情况下,先声明的Adice优先级更高,更好的解决方案是直接在Advisor上指定它们的order值,值越小,优先级越高。
在Pointcut、Advice、Aspect等各种元素都已准备好的情况下,我们还需要准备的就是织入器了。AspectJ采用ajc编辑器作为它的织入器,JBoss AOP使用自定义的ClassLoader作织入器,Spring AOP使用类ProxyFactory作为织入器。注意,ProxyFactory并不是Spring中的唯一织入器,而是最常见织入器。使用织入器,我们只需要为其提供两样最基本的东西。
+ 将要织入的目标对象
+ 要应用的Aspect,也就是Advisor。
我们知道Spring内部使用了动态代理和CGLIB两种机制,所以ProxyFactory内部会根据一定的算法自动在这两个机制之间切换。注意:对于Advice和Advisor,Introduction的部分是不能与其他Advice和Advisor混用的。 我们知道,Spring中关于AOP的实现是通过ProxyFactory类来实现的。现在我们来看看ProxyFactory的具体细节,首先看它的根部AopProxy接口。
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
Spring AOP框架使用AopProxy对不同代理实现机制进行抽象。其中AopProxy的相关结构图如下所示:
可以看到,不同的AopProxy实现的实例化过程采用了抽象工厂模式。
随着JDK和Spring的升级,现在Spring AOP的使用方式已经包含有三种形式了,他们分别是基于接口和XSD的第一代AOP、基于注解的第二代AOP、基于Schema的第二代AOP,虽然配置方式多了,但他们的效果是相同的,可以根据自己的实际情况进行选择。
下面我们来讲讲AOP的应用场景和案例:
+ 异常处理 主要针对unchecked exception
+ 安全检查 Spring security
+ 缓存
AOP虽然很好地解决了等多系统需求的问题,下面我们就来看看在使用AOP的过程中应该注意的一些问题。
+ 公开当前调用的代理对象的探讨