Spring 中的 Pointcut API

本節描述了 Spring 如何處理關鍵的切入點概念。

概念

Spring 的切入點模型支援獨立於通知型別重用切入點。你可以用相同的切入點針對不同的通知。

org.springframework.aop.Pointcut 介面是核心介面,用於將通知定位到特定的類和方法。完整的介面如下:

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 代理時執行,以避免在每次方法呼叫時進行測試。如果兩引數的 matches 方法對於給定方法返回 true,並且 MethodMatcher 的 isRuntime() 方法返回 true,則在每次方法呼叫時都會呼叫三引數的 matches 方法。這允許切入點在目標通知開始之前立即檢視傳遞給方法呼叫的引數。

大多數 MethodMatcher 實現都是靜態的,這意味著它們的 isRuntime() 方法返回 false。在這種情況下,三引數的 matches 方法永遠不會被呼叫。

如果可能,請嘗試使切入點靜態,允許 AOP 框架在建立 AOP 代理時快取切入點評估的結果。

切入點上的操作

Spring 支援切入點上的操作(特別是並集和交集)。

並集表示兩個切入點中任何一個匹配的方法。交集表示兩個切入點都匹配的方法。並集通常更有用。你可以透過使用 org.springframework.aop.support.Pointcuts 類中的靜態方法或使用同一包中的 ComposablePointcut 類來組合切入點。然而,使用 AspectJ 切入點表示式通常是一種更簡單的方法。

AspectJ 表示式切入點

自 2.0 版以來,Spring 使用的最重要的切入點型別是 org.springframework.aop.aspectj.AspectJExpressionPointcut。這是一個使用 AspectJ 提供的庫來解析 AspectJ 切入點表示式字串的切入點。

有關支援的 AspectJ 切入點原語的討論,請參閱前一章

便捷切入點實現

Spring 提供了幾種便捷的切入點實現。你可以直接使用其中一些;其他一些則旨在在應用程式特定的切入點中進行子類化。

靜態切入點

靜態切入點基於方法和目標類,不能考慮方法的引數。靜態切入點對於大多數用法來說已經足夠,並且是最好的選擇。Spring 只在方法首次呼叫時評估一次靜態切入點。之後,無需在每次方法呼叫時再次評估切入點。

本節的其餘部分描述了 Spring 中包含的一些靜態切入點實現。

正則表示式切入點

指定靜態切入點的一種顯而易見的方式是正則表示式。除了 Spring 之外,還有幾個 AOP 框架也實現了這一點。org.springframework.aop.support.JdkRegexpMethodPointcut 是一個通用的正則表示式切入點,它使用 JDK 中的正則表示式支援。

使用 JdkRegexpMethodPointcut 類,你可以提供一個模式字串列表。如果其中任何一個匹配,則切入點評估為 true。(因此,生成的切入點實際上是指定模式的並集。)

以下示例演示瞭如何使用 JdkRegexpMethodPointcut

  • Java

  • Kotlin

  • Xml

@Configuration
public class JdkRegexpConfiguration {

	@Bean
	public JdkRegexpMethodPointcut settersAndAbsquatulatePointcut() {
		JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
		pointcut.setPatterns(".*set.*", ".*absquatulate");
		return pointcut;
	}
}
@Configuration
class JdkRegexpConfiguration {

	@Bean
	fun settersAndAbsquatulatePointcut() = JdkRegexpMethodPointcut().apply {
		setPatterns(".*set.*", ".*absquatulate")
	}
}
<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 封裝了切入點和通知,如下例所示:

  • Java

  • Kotlin

  • Xml

@Configuration
public class RegexpConfiguration {

	@Bean
	public RegexpMethodPointcutAdvisor settersAndAbsquatulateAdvisor(Advice beanNameOfAopAllianceInterceptor) {
		RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
		advisor.setAdvice(beanNameOfAopAllianceInterceptor);
		advisor.setPatterns(".*set.*", ".*absquatulate");
		return advisor;
	}
}
@Configuration
class RegexpConfiguration {

	@Bean
	fun settersAndAbsquatulateAdvisor(beanNameOfAopAllianceInterceptor: Advice) = RegexpMethodPointcutAdvisor().apply {
		advice = beanNameOfAopAllianceInterceptor
		setPatterns(".*set.*", ".*absquatulate")
	}
}
<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 控制流切入點在概念上類似於 AspectJ 的 cflow 切入點,儘管功能較弱。(目前無法指定切入點在另一個切入點匹配的連線點之下執行。)控制流切入點匹配當前的呼叫棧。例如,如果連線點是由 com.mycompany.web 包中的方法或 SomeCaller 類呼叫的,它可能會觸發。控制流切入點透過使用 org.springframework.aop.support.ControlFlowPointcut 類來指定。

控制流切入點在執行時評估成本顯著高於其他動態切入點。在 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 中的切入點是 Java 類而不是語言特性(如 AspectJ),因此你可以宣告自定義切入點,無論是靜態的還是動態的。Spring 中的自定義切入點可以任意複雜。但是,如果可以,我們建議使用 AspectJ 切入點表示式語言。

Spring 的後續版本可能會提供對 JAC 提供的“語義切入點”的支援——例如,“所有更改目標物件中例項變數的方法”。
© . This site is unofficial and not affiliated with VMware.