6.1Pointcut API

本节介绍Spring如何处理关键的切入点概念。

6.1.1 概念

Spring的切入点模型支持独立于通知类型的切入点重用。你可以使用相同的切入点来通知不同的建议目标。

org.springframework.aop.Pointcut接口是一个中心接口,用于将通知定向到特定的类和方法。完整interface如下:

public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}

将Pointcut接口拆分为两部分允许重用类和方法匹配部分以及细粒度组合操作(例如,使用另一个方法匹配器执行“联合”)。

ClassFilter接口用于将切入点限制为一组给定的目标类。如果matches()方法始终返回true,则所有目标类都匹配。下面的列表显示ClassFilter接口定义:

public interface ClassFilter {
boolean matches(Class clazz);
}

MethodMatcher接口通常来说更加重要。完整的接口如下:

public interface MethodMatcher {
boolean matches(Method m, Class targetClass);
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}

matches(method,class)方法用于测试此切入点是否与目标类上的给定方法匹配。当创建AOP代理时可以执行此评估,以避免对每个方法调用进行测试。如果两个参数匹配的方法对于给定的方法返回true,而methodMatcher的isRuntime()方法返回true,则在每次方法调用时都会调用三个参数匹配的方法。这允许切入点查看在执行目标通知之前立即传递给方法调用的参数。

大多数MethodMatcher实现是静态的,这意味着它们的isRuntime()方法返回false。在本例中,从不调用三个参数matches方法。

如果可能,尝试使切入点静态化,允许AOP框架在创建AOP代理时缓存切入点评估的结果。

6.1.2 Pointcuts的操作

Spring支持对切入点的操作(尤其是Union和intersection)。

Union是指切入点匹配任意一个方法。intersection表示切入点同时匹配的方法。Union通常更有用。你可以使用org.springframework.aop.support.Pointcuts类中的静态方法或在同一个包中使用ComposablePointcut类来撰写切入点。然而,使用AspectJ切入点表达式通常是一种更简单的方法。

6.1.3 AspectJ表达式Pointcuts

从2.0开始,Spring使用的最重要的切入点类型是org.springframework.aop.aspectj.AspectJExpressionPointcut。这是一个切入点,它使用AspectJ提供的库来分析AspectJ切入点表达式字符串。

有关支持的aspectj切入点原语的讨论,请参阅上一章。

6.1.4 方便的Pointcut实现

Spring提供了几个方便的切入点实现。你可以直接使用其中的一些。其他的则被用在特定于应用程序的切入点中进行子类化。

静态切入点

静态切入点基于方法和目标类,不能考虑方法的参数。静态切入点对于大多数使用来说是最好的。当第一次调用方法时,Spring只能计算一次静态切入点。之后,不需要对每个方法调用再次计算切入点。 本节的其余部分描述了Spring中包含的一些静态切入点实现。

正则表达式切入点

指定静态切入点的一个明显方法是正则表达式。除了Spring,还有几个AOP框架使这成为可能。org.springframework.aop.support.JdkRegexpMethodPointcut是一个通用的正则表达式切入点,它使用JDK中的正则表达式支持。

通过JdkRegexpMethodPointcut类,可以提供模式字符串的列表。如果其中任何一个匹配,则切入点的计算结果为true。(因此,结果实际上是这些切点的并集。)

下面的示例演示如何使用JdkRegexpMethodPointcut:

<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>

Spring提供了一个名为RegexpMethodPointcutAdvisor的方便类,它还允许我们引用一个Advice(请记住,Advice可以是拦截器,在建议、抛出建议和其他建议之前)。在这背后,Spring使用了JdkRegexpMethodPointcut。使用RegexpMethodPointcutAdvisor简化了连接,因为一个bean同时封装了pointcut和advice,如下示例所示:

<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>

你可以在任何Advice类型中使用RegexpMethodPointcutAdvisor。

属性驱动Pointcuts

一种重要的静态切入点类型是元数据驱动的切入点。这将使用元数据属性的值(通常是源级元数据)。

动态pointcuts

动态切入点的计算成本比静态切入点高。它们考虑了方法参数和静态信息。这意味着必须用每个方法调用对它们进行评估,并且不能缓存结果,因为参数会有所不同。

主要的例子是控制流切入点。

控制流切入点

Spring控制流切入点在概念上与AspectJ CFlow切入点相似,但功能较弱。(目前无法指定在与另一个切入点匹配的连接点下方执行切入点。)控制流切入点与当前调用堆栈匹配。例如,如果连接点是由com.mycompany.web包中的方法或SomeCaller类调用的,则它可能会激发。通常使用org.springframework.aop.support.ControlFlowPointcut类指定控制流切入点。

控制流切入点在运行时的计算成本比其他动态切入点都要高。在Java 1.4中,成本是其他动态切入点的五倍。

6.1.5 Pointcut 父类

Spring提供了有用的切入点超类来帮助你实现自己的切入点。

因为静态切入点最有用,所以你可能应该将StaticMethodMatcherPointCut子类化。这只需要实现一个抽象方法(尽管你可以重写其他方法来定制行为)。下面的示例显示如何将StaticMethodMatcherPointCut子类化:

class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}

还有动态切入点的超类。你可以对任何通知类型使用自定义切入点。

6.1.6 自定义Pointcuts

因为Spring AOP中的切入点是Java类,而不是语言特征(如AspectJ中),所以可以声明自定义切入点,无论是静态的还是动态的。Spring中的自定义切入点可以任意复杂。但是,如果可以的话,我们建议使用AspectJ切入点表达式语言。

Spring的较新版本可能支持jac-提供的“语义切入点”,例如,“所有更改目标对象中实例变量的方法”。