Spring 中的切入點 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>
你可以搭配任何 Advice
型別使用 RegexpMethodPointcutAdvisor
。
切入點超類
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
}
}
動態切入點也有超類。你可以搭配任何通知型別使用自定義切入點。