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 提供了有用的切入點超類來幫助你實現自己的切入點。
由於靜態切入點最有用,你可能應該子類化 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
}
}
動態切入點也有超類。你可以將自定義切入點與任何通知型別一起使用。