第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接口定义如以下代码所示:

 
    
    public interface Pointcut {
      ClassFilter getClassFilter();
      MethodMatcher getMethodMatcher();
      Pointcut TRUE = TruePointcut.INSTANCE;
    }
    

ClassFilterMethodMatcher分别用于匹配将 被执行织入操作的对象 以及 相应的方法 。之所以将类型匹配和方法匹配分开定义,是因为可以重用不同级别的匹配定义,并且可以在不同的级别或者相同的级别上进行组合操作,或者强制让某个子类只覆写(Override)相应的方法定义等。

ClassFilter接口的作用是对Joinpoint所处的对象进行Class级别的类型匹配,其定义如下:

 
    
    public interface ClassFilter {
      boolean matches(Class clazz);
      ClassFilter TRUE = TrueClassFilter.INSTANCE;
    }
    
 

当织入的目标对象的Class类型与Pointcut所规定的类型相符时,matches方法将会返回true,否则,返回false,即意味着不会对这个类型的目标对象进行织入操作。比如,如果我们仅希望对系统中Foo类型的类执行织入,则可以如下这样定义ClassFilter

 
    public class FooClassFilter {
      public boolean matches(Class clazz) {
        return Foo.class.equals(clazz);
      }
    }
    

当然,如果类型与我们所捕捉的Joinpoint无关,那么Pointcut中使用的ClassFilter可以直接使用ClassFilter TRUE =TrueClassFilter.INSTANCE;。当Pointcut中返回的ClassFilter类型为该类型实例时,Pointcut的匹配将会针对系统中所有的目标类以及它们的实例进行。

相对于ClassFilter的简单定义,MethodMatcher则要复杂得多。毕竟,Spring主要支持的就是方法级别的拦截——“重头戏”可不能单薄啊!MethodMatcher定义如下:

    public interface MethodMatcher {
      boolean matches(Method method, Class targetClass);
      boolean isRuntime();
      boolean matches(Method method, Class targetClass, Object[] args);
      MethodMatcher TRUE = TrueMethodMathcer.INSTANCE;
    }

MethodMatcher通过重载(Overload),定义了两个matches方法,而这两个方法的分界线就是isRuntime()方法。在对对象具体方法进行拦截的时候,可以忽略每次方法执行的时候调用者传入的参数,也可以每次都检查这些方法调用参数,以强化拦截条件。假设对以下方法进行拦截:

    public boolean login(String username, String password);

如果只想在login方法之前插入计数功能,那么login方法的参数对于Joinpoint捕捉就是可以忽略的。而如果想在用户登录的时候对某个用户做单独处理,如不让其登录或者给予特殊权限,那么这个方法的参数就是在匹配Joinpoint的时候必须要考虑的。

(1)在前一种情况下,isRuntime返回false,表示不会考虑具体Joinpoint的方法参数,这种类型的MethodMatcher称之为StaticMethodMatcher

因为不用每次都检查参数,那么对于同样类型的方法匹配结果,就可以在框架内部缓存以提高性能。isRuntime方法返回false表明当前的MethodMatcherStaticMethodMatcher的时候,只有boolean matches(Method method, Class targetClass);方法将被执行,它的匹配结果将会成为其所属的Pointcut主要依据。

(2)当isRuntime方法返回true时,表明该MethodMatcher将会每次都对方法调用的参数进行匹配检查,这种类型的MethodMatcher称之为DynamicMethodMatcher

因为每次都要对方法参数进行检查,无法对匹配的结果进行缓存,所以,匹配效率相对于staticMethodMatcher来说要差。而且大部分情况下,staticMethodMatcher已经可以满足需要,最好避免使用DynamicMethodMatcher类型。

如果一个MethodMatcherDynamicMethodMatcher(isRuntime()返回true),并且当方法boolean matches(Method method,Class targetClass);也返回true的时候,三个参数的matches方法将被执行,以进一步检查匹配条件。

如果方法boolean matches(Method method,Class targetClass);返回false,那么不管这个MethodMatcherStaticMethodMatcher还是DynamicMethodMatcher,该结果已经是最终的匹配结果——你可以猜得到,三个参数的matches方法那铁定是执行不了了。

MethodMatcher类型的基础上,Pointcut可以分为两类,即staticMethodMatcherPointcutDynamicMethodMatcherPointcut.因为staticMethodMatcherPointcut具有明显的性能优势,所以,Spring为其提供了更多支持。

常见的Pointcut

不做介绍 有兴趣可以看原文

扩展Pointcut(Customize Pointcut)

不做介绍 有兴趣可以看原文

IoC容器中Pointcut

不做介绍 有兴趣可以看原文