第9章 Spring AOP一世
本章内容
-
Spring AOP中的Joinpoint
-
Spring AOP中的Pointcut
-
Spring AOP中的Advice
-
Spring AOP中的Aspect
-
Spring AOP中的织入
-
TargetSource
在动态代理和CGLIB的支持下,SpringAOP框架的实现经过了两代。从Spring的AOP框架第一次发布,到Spring2.0发布之前的AOP实现,是Spring第一-代AOP实现。Spring2.0发布后的AOP实现是第二代。
不过划分归划分,SpringAOP的底层实现机制却一直没变。唯一改变的,是各种AOP概念实体的 表现形式 以及Spring AOP的使用方式 。
下面,我们先从第一代的SpringAOP相关的概念实体说起。
Spring AOP中的Joinpoint
之前我们已经提到,AOP的Joinpoint
可以有许多种类型,如构造方法调用、字段的设置及获取、方法调用、方法执行等。但是,在SpringAOP中,仅支持方法级别的Joinpoint 。更确切地说,只支持方法执行(Method Execution)类型的Joinpoint
。
这一点我们从8.2节就能看出来。虽然Spring AOP仅提供方法拦截,但是在实际的开发过程中,这已经可以满足80%的开发需求了。所以,我们不用过于担心SpringAOP的能力。
SpringAOP之所以如此,主要有以下几个原因。
(1)前面说过了,SpringAOP要提供一个简单而强大的AOP框架,并不想因大而全使得框架本身过于臃肿。
(2)对于类中属性(Field
)级别的Joinpoint
,如果提供这个级别的拦截支持,那么就破坏了面向对象的封装,而且,完全可以通过对setter和getter方法的拦截达到同样的目的。
(3)如果应用需求非常特殊,完全超出了SpringAOP提供的那80%的需求支持,不妨求助于现有的其他AOP实现产品,如AspectJ。目前来看,AspectJ是Java平台对AOP支持最完善的产品,同时,SpringAOP也提供了对AspectJ的支持。(不过,需要注意的是,AspectJ也不是完全支持所有类型的Joinpoint
,如程序中的循环结构。部分原因应该归结为要实现这20%的需求,可能需要付出80%的工作和努力。)
Spring AOP中的Pointcut
Spring中以接口定义org.springframework.aop.Pointcut
作为其AOP框架中所有Pointcut
的最顶层抽象,该接口定义了两个方法用来帮助捕捉系统中的相应Joinpoint
,并提供了一个TruePointcut
类型实例。如果Pointcut
类型为TruePointcut
,默认会对系统中的所有对象,以及对象上所有被支持的Joinpoint
进行匹配。
org.springframework.aop.Pointcut
接口定义如以下代码所示:
ClassFilter
和MethodMatcher
分别用于匹配将 被执行织入操作的对象 以及 相应的方法
。之所以将类型匹配和方法匹配分开定义,是因为可以重用不同级别的匹配定义,并且可以在不同的级别或者相同的级别上进行组合操作,或者强制让某个子类只覆写(Override
)相应的方法定义等。
ClassFilter
接口的作用是对Joinpoint
所处的对象进行Class
级别的类型匹配,其定义如下:
当织入的目标对象的Class类型与Pointcut所规定的类型相符时,matches方法将会返回true,否则,返回false,即意味着不会对这个类型的目标对象进行织入操作。比如,如果我们仅希望对系统中Foo类型的类执行织入,则可以如下这样定义ClassFilter
:
当然,如果类型与我们所捕捉的Joinpoint无关,那么Pointcut中使用的ClassFilter可以直接使用ClassFilter TRUE =TrueClassFilter.INSTANCE;
。当Pointcut中返回的ClassFilter类型为该类型实例时,Pointcut的匹配将会针对系统中所有的目标类以及它们的实例进行。
相对于ClassFilter
的简单定义,MethodMatcher
则要复杂得多。毕竟,Spring主要支持的就是方法级别的拦截——“重头戏”可不能单薄啊!MethodMatcher
定义如下:
MethodMatcher
通过重载(Overload),定义了两个matches方法,而这两个方法的分界线就是isRuntime()
方法。在对对象具体方法进行拦截的时候,可以忽略每次方法执行的时候调用者传入的参数,也可以每次都检查这些方法调用参数,以强化拦截条件。假设对以下方法进行拦截:
如果只想在login方法之前插入计数功能,那么login方法的参数对于Joinpoint捕捉就是可以忽略的。而如果想在用户登录的时候对某个用户做单独处理,如不让其登录或者给予特殊权限,那么这个方法的参数就是在匹配Joinpoint的时候必须要考虑的。
(1)在前一种情况下,isRuntime返回false,表示不会考虑具体Joinpoint的方法参数,这种类型的MethodMatcher
称之为StaticMethodMatcher
。
因为不用每次都检查参数,那么对于同样类型的方法匹配结果,就可以在框架内部缓存以提高性能。isRuntime方法返回false表明当前的MethodMatcher
为StaticMethodMatcher
的时候,只有boolean matches(Method method, Class targetClass);
方法将被执行,它的匹配结果将会成为其所属的Pointcut主要依据。
(2)当isRuntime方法返回true时,表明该MethodMatcher
将会每次都对方法调用的参数进行匹配检查,这种类型的MethodMatcher
称之为DynamicMethodMatcher
。
因为每次都要对方法参数进行检查,无法对匹配的结果进行缓存,所以,匹配效率相对于staticMethodMatcher
来说要差。而且大部分情况下,staticMethodMatcher
已经可以满足需要,最好避免使用DynamicMethodMatcher
类型。
如果一个MethodMatcher
为DynamicMethodMatcher
(isRuntime()返回true),并且当方法boolean matches(Method method,Class targetClass);
也返回true的时候,三个参数的matches方法将被执行,以进一步检查匹配条件。
如果方法boolean matches(Method method,Class targetClass);
返回false,那么不管这个MethodMatcher
是StaticMethodMatcher
还是DynamicMethodMatcher,
该结果已经是最终的匹配结果——你可以猜得到,三个参数的matches方法那铁定是执行不了了。
在MethodMatcher
类型的基础上,Pointcut可以分为两类,即staticMethodMatcherPointcut
和DynamicMethodMatcherPointcut
.因为staticMethodMatcherPointcut
具有明显的性能优势,所以,Spring为其提供了更多支持。
常见的Pointcut
不做介绍 有兴趣可以看原文
扩展Pointcut(Customize Pointcut)
不做介绍 有兴趣可以看原文
IoC容器中Pointcut
不做介绍 有兴趣可以看原文