|
对于最新稳定版本,请使用 Spring Framework 7.0.6! |
Spring 中的切入点(Pointcut)API
本节介绍 Spring 如何处理关键的切入点(pointcut)概念。
概念
Spring 的切入点模型支持独立于通知类型的切入点复用。你可以使用相同的切入点来应用不同类型的通知。
org.springframework.aop.Pointcut 接口是核心接口,用于将通知(advice)定向到特定的类和方法。该接口的完整定义如下:
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) 方法用于测试该切入点(pointcut)是否曾匹配目标类上的某个给定方法。此评估可在创建 AOP 代理时执行,从而避免在每次方法调用时都进行测试。如果针对某个给定方法,双参数的 matches 方法返回 true,并且该 MethodMatcher 的 isRuntime() 方法也返回 true,那么每次方法调用时都会调用三参数的 matches 方法。这使得切入点可以在目标通知(advice)开始执行前,立即检查传递给方法调用的参数。
大多数 MethodMatcher 实现都是静态的,这意味着它们的 isRuntime() 方法返回 false。在这种情况下,三个参数的 matches 方法永远不会被调用。
| 如果可能,请尽量将切入点(pointcut)设为静态的,以便在创建 AOP 代理时,AOP 框架能够缓存切入点评估的结果。 |
切点操作
Spring 支持对切入点(pointcut)进行操作(特别是并集和交集)。
联合(Union)表示匹配任一切点的方法。
交集(Intersection)表示同时匹配两个切点的方法。
通常,联合更有用。
你可以通过使用 org.springframework.aop.support.Pointcuts 类中的静态方法,或者使用同一包中的 ComposablePointcut 类来组合切点。然而,使用 AspectJ 切点表达式通常是更简单的方法。
AspectJ 表达式切点
从 2.0 版本开始,Spring 使用的最重要的一类切入点(pointcut)是
org.springframework.aop.aspectj.AspectJExpressionPointcut。这是一种使用 AspectJ 提供的库来解析 AspectJ 切入点表达式字符串的切入点。
有关支持的 AspectJ 切入点原语的讨论,请参见上一章。
便捷的切点实现
Spring 提供了多种便捷的切入点(pointcut)实现。其中一些可直接使用;另一些则旨在被子类化,以创建特定于应用程序的切入点。
静态切点
静态切入点基于方法和目标类,无法考虑方法的参数。在大多数使用场景中,静态切入点已足够,并且是最佳选择。 Spring 仅在方法首次被调用时对静态切入点进行一次评估。 此后,在每次方法调用时就无需再次评估该切入点。
本节其余部分将介绍 Spring 自带的一些静态切入点(static pointcut)实现。
正则表达式切点
指定静态切入点的一种显而易见的方法是使用正则表达式。除了 Spring 之外,还有多个 AOP 框架也支持这种方式。
org.springframework.aop.support.JdkRegexpMethodPointcut 是一个通用的正则表达式切入点,它利用了 JDK 中提供的正则表达式支持。
使用 JdkRegexpMethodPointcut 类,您可以提供一组模式字符串。
只要其中任意一个模式匹配成功,该切入点(pointcut)就会评估为 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>
你可以将 RegexpMethodPointcutAdvisor 与任何 Advice 类型一起使用。
动态切点
动态切入点的评估开销比静态切入点更高。它们不仅考虑静态信息,还会考虑方法参数。这意味着每次方法调用时都必须对其进行评估,并且无法缓存评估结果,因为参数会有所不同。
主要示例是control flow切入点。
控制流切点
Spring 的控制流切入点(control flow pointcut)在概念上类似于 AspectJ 的 cflow 切入点,但功能较弱。(目前无法指定某个切入点在另一个切入点所匹配的连接点之下运行。)控制流切入点会匹配当前的调用栈。例如,如果连接点是由 com.mycompany.web 包中的某个方法或由 SomeCaller 类所调用的,该切入点就可能被触发。控制流切入点通过使用 org.springframework.aop.support.ControlFlowPointcut 类来指定。
| 控制流切入点(control flow pointcuts)在运行时的评估开销显著高于其他动态切入点,即使与其他动态切入点相比也是如此。在 Java 1.4 中,其开销大约是其他动态切入点的五倍。 |
切点超类
Spring 提供了有用的切入点超类,以帮助您实现自己的切入点。
由于静态切入点最为有用,你很可能应该继承
StaticMethodMatcherPointcut 类。这仅需实现一个
抽象方法(尽管你可以重写其他方法以自定义行为)。以下示例展示了如何继承 StaticMethodMatcherPointcut:
-
Java
-
Kotlin
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}
class TestStaticPointcut : StaticMethodMatcherPointcut() {
override fun matches(method: Method, targetClass: Class<*>): Boolean {
// return true if custom criteria match
}
}
此外,动态切入点也有对应的超类。 您可以将自定义切入点与任何类型的通知一起使用。
自定义切点
由于 Spring AOP 中的切入点(pointcut)是 Java 类,而非语言特性(如 AspectJ 中那样),因此您可以声明自定义的切入点,无论是静态的还是动态的。Spring 中的自定义切入点可以具有任意复杂的逻辑。然而,如果可能的话,我们建议您使用 AspectJ 切入点表达式语言。
| Spring 的后续版本可能会提供对 JAC 所提供的“语义切入点”(semantic pointcuts)的支持——例如,“所有更改目标对象实例变量的方法”。 |