对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

Spring 中的切入点(Pointcut)API

本节介绍 Spring 如何处理关键的切入点(pointcut)概念。spring-doc.cadn.net.cn

概念

Spring 的切入点模型支持独立于通知类型的切入点复用。你可以使用相同的切入点来应用不同类型的通知。spring-doc.cadn.net.cn

org.springframework.aop.Pointcut 接口是核心接口,用于将通知(advice)定向到特定的类和方法。该接口的完整定义如下:spring-doc.cadn.net.cn

public interface Pointcut {

	ClassFilter getClassFilter();

	MethodMatcher getMethodMatcher();
}

Pointcut 接口拆分为两个部分,可以复用类匹配和方法匹配的部分,并支持细粒度的组合操作(例如与另一个方法匹配器执行“并集”操作)。spring-doc.cadn.net.cn

ClassFilter 接口用于将切入点限制在给定的目标类集合中。如果 matches() 方法始终返回 true,则匹配所有目标类。以下代码清单展示了 ClassFilter 接口的定义:spring-doc.cadn.net.cn

public interface ClassFilter {

	boolean matches(Class clazz);
}

MethodMatcher 接口通常更为重要。完整的接口如下:spring-doc.cadn.net.cn

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)开始执行前,立即检查传递给方法调用的参数。spring-doc.cadn.net.cn

大多数 MethodMatcher 实现都是静态的,这意味着它们的 isRuntime() 方法返回 false。在这种情况下,三个参数的 matches 方法永远不会被调用。spring-doc.cadn.net.cn

如果可能,请尽量将切入点(pointcut)设为静态的,以便在创建 AOP 代理时,AOP 框架能够缓存切入点评估的结果。

切点操作

Spring 支持对切入点(pointcut)进行操作(特别是并集和交集)。spring-doc.cadn.net.cn

联合(Union)表示匹配任一切点的方法。 交集(Intersection)表示同时匹配两个切点的方法。 通常,联合更有用。 你可以通过使用 org.springframework.aop.support.Pointcuts 类中的静态方法,或者使用同一包中的 ComposablePointcut 类来组合切点。然而,使用 AspectJ 切点表达式通常是更简单的方法。spring-doc.cadn.net.cn

AspectJ 表达式切点

从 2.0 版本开始,Spring 使用的最重要的一类切入点(pointcut)是 org.springframework.aop.aspectj.AspectJExpressionPointcut。这是一种使用 AspectJ 提供的库来解析 AspectJ 切入点表达式字符串的切入点。spring-doc.cadn.net.cn

有关支持的 AspectJ 切入点原语的讨论,请参见上一章spring-doc.cadn.net.cn

便捷的切点实现

Spring 提供了多种便捷的切入点(pointcut)实现。其中一些可直接使用;另一些则旨在被子类化,以创建特定于应用程序的切入点。spring-doc.cadn.net.cn

静态切点

静态切入点基于方法和目标类,无法考虑方法的参数。在大多数使用场景中,静态切入点已足够,并且是最佳选择。 Spring 仅在方法首次被调用时对静态切入点进行一次评估。 此后,在每次方法调用时就无需再次评估该切入点。spring-doc.cadn.net.cn

本节其余部分将介绍 Spring 自带的一些静态切入点(static pointcut)实现。spring-doc.cadn.net.cn

正则表达式切点

指定静态切入点的一种显而易见的方法是使用正则表达式。除了 Spring 之外,还有多个 AOP 框架也支持这种方式。 org.springframework.aop.support.JdkRegexpMethodPointcut 是一个通用的正则表达式切入点,它利用了 JDK 中提供的正则表达式支持。spring-doc.cadn.net.cn

使用 JdkRegexpMethodPointcut 类,您可以提供一组模式字符串。 只要其中任意一个模式匹配成功,该切入点(pointcut)就会评估为 true。(因此, 最终的切入点实际上等效于所指定模式的并集。)spring-doc.cadn.net.cn

下面的例子展示了如何使用JdkRegexpMethodPointcutspring-doc.cadn.net.cn

<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),如下例所示:spring-doc.cadn.net.cn

<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 类型一起使用。spring-doc.cadn.net.cn

基于属性的切点

一种重要的静态切入点是元数据驱动的切入点。它使用元数据属性的值(通常是源代码级别的元数据)。spring-doc.cadn.net.cn

动态切点

动态切入点的评估开销比静态切入点更高。它们不仅考虑静态信息,还会考虑方法参数。这意味着每次方法调用时都必须对其进行评估,并且无法缓存评估结果,因为参数会有所不同。spring-doc.cadn.net.cn

主要示例是control flow切入点。spring-doc.cadn.net.cn

控制流切点

Spring 的控制流切入点(control flow pointcut)在概念上类似于 AspectJ 的 cflow 切入点,但功能较弱。(目前无法指定某个切入点在另一个切入点所匹配的连接点之下运行。)控制流切入点会匹配当前的调用栈。例如,如果连接点是由 com.mycompany.web 包中的某个方法或由 SomeCaller 类所调用的,该切入点就可能被触发。控制流切入点通过使用 org.springframework.aop.support.ControlFlowPointcut 类来指定。spring-doc.cadn.net.cn

控制流切入点(control flow pointcuts)在运行时的评估开销显著高于其他动态切入点,即使与其他动态切入点相比也是如此。在 Java 1.4 中,其开销大约是其他动态切入点的五倍。

切点超类

Spring 提供了有用的切入点超类,以帮助您实现自己的切入点。spring-doc.cadn.net.cn

由于静态切入点最为有用,你很可能应该继承 StaticMethodMatcherPointcut 类。这仅需实现一个 抽象方法(尽管你可以重写其他方法以自定义行为)。以下示例展示了如何继承 StaticMethodMatcherPointcutspring-doc.cadn.net.cn

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-doc.cadn.net.cn

自定义切点

由于 Spring AOP 中的切入点(pointcut)是 Java 类,而非语言特性(如 AspectJ 中那样),因此您可以声明自定义的切入点,无论是静态的还是动态的。Spring 中的自定义切入点可以具有任意复杂的逻辑。然而,如果可能的话,我们建议您使用 AspectJ 切入点表达式语言。spring-doc.cadn.net.cn

Spring 的后续版本可能会提供对 JAC 所提供的“语义切入点”(semantic pointcuts)的支持——例如,“所有更改目标对象实例变量的方法”。