?
? ????? PHP ??? ???? ??? ?? ??
讓我們看看Spring是如何處理切入點(diǎn)這個重要概念的。
Spring的切入點(diǎn)模型使得切入點(diǎn)可以獨(dú)立于通知(advice)類型進(jìn)行重用,這就使得針對不同通知使用相同的pointcut成為可能。
org.springframework.aop.Pointcut
是最核心的接口,用來將通知應(yīng)用于特定的類和方法,完整的接口定義如下:
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); }
將Pointcut
接口分割成有利于重用類和方法匹配的兩部分,
以及進(jìn)行更細(xì)粒度的操作組合(例如與另一個方法匹配實(shí)現(xiàn)進(jìn)行“或操作”)。
ClassFilter
接口用來將切入點(diǎn)限定在一個給定的類集合中。
如果matches()
方法總是返回true,所有目標(biāo)類都將被匹配:
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)
方法用來測試這個切入點(diǎn)是否匹配目標(biāo)類的指定方法。
這將在AOP代理被創(chuàng)建的時候進(jìn)行運(yùn)算,這樣可以避免在每次方法調(diào)用的時候都運(yùn)算。
如果matches(Method, Class)
對于一個給定的方法返回true,并且isRuntime()
也返回true,
那么matches(Method, Class, Object[])
將在每個方法調(diào)用的時候被調(diào)用。
這使得切入點(diǎn)在通知將被執(zhí)行前可以查看傳入到方法的參數(shù)。
大多數(shù)MethodMatcher是靜態(tài)的,這意味著isRuntime()
方法返回false。
在這種情況下,matches(Method, Class , Object[])
永遠(yuǎn)不會被調(diào)用。
應(yīng)盡可能地使切入點(diǎn)保持靜態(tài),這就允許AOP框架在AOP代理被創(chuàng)建時緩存對切入點(diǎn)的計(jì)算結(jié)果。
Spring在切入點(diǎn)上支持以下運(yùn)算: 或和與。
或運(yùn)算表示只需有一個切入點(diǎn)被匹配就執(zhí)行方法。
與運(yùn)算表示所有的切入點(diǎn)都匹配的情況下才執(zhí)行。
通?;蜻\(yùn)算要更有用一些。
切入點(diǎn)可以使用org.springframework.aop.support.Pointcuts類中的靜態(tài)方法來進(jìn)行組合, 或者使用同一個包內(nèi)的ComposablePointcut類。不過使用AspectJ切入點(diǎn)表達(dá)式通常會更簡單一些。
從2.0開始,Spring中使用的最重要的切入點(diǎn)類型是org.springframework.aop.aspectj.AspectJExpressionPointcut
。
這個切入點(diǎn)使用AspectJ提供的庫來解析滿足AspectJ語法切入點(diǎn)表達(dá)式字符串。
可以查看前一章關(guān)于所支持的AspectJ切入點(diǎn)原語的討論。
Spring提供了一些很方便的切入點(diǎn)實(shí)現(xiàn)。一些可直接使用,其它的則是切入點(diǎn)應(yīng)用規(guī)范的子集。
靜態(tài)切入點(diǎn)基于方法和目標(biāo)類進(jìn)行切入點(diǎn)判斷,不考慮方法的參數(shù)。在多數(shù)情況下,靜態(tài)切入點(diǎn)是高效的、最好的選擇。 Spring只在第一次調(diào)用方法時運(yùn)算靜態(tài)切入點(diǎn):以后每次調(diào)用這個方法時就不需要再運(yùn)算了。
讓我們考慮Spring中的一些靜態(tài)切入點(diǎn)實(shí)現(xiàn)。
顯而易見,一種描述靜態(tài)切入點(diǎn)的方式是使用正則表達(dá)式。包含Spring在內(nèi)的一些AOP框架都支持這種方式。
org.springframework.aop.support.Perl5RegexpMethodPointcut
是一個最基本的正則表達(dá)式切入點(diǎn),
它使用Perl 5正則表達(dá)式語法。Perl5RegexpMethodPointcut
依賴Jakarta ORO進(jìn)行正則表達(dá)式匹配。
Spring也提供了JdkRegexpMethodPointcut
類,它使用JDK 1.4或更高版本里提供的正則表達(dá)式支持。
使用Perl5RegexpMethodPointcut
類,你可以提供一組模式字符串。
如果其中任意一個模式匹配,切入點(diǎn)將被解析為true。(實(shí)際上就是這些切入點(diǎn)的或集。)
用法如下:
<bean id="settersAndAbsquatulatePointcut" class="org.springframework.aop.support.Perl5RegexpMethodPointcut"> <property name="patterns"> <list> <value>.*set.*</value> <value>.*absquatulate</value> </list> </property> </bean>
Spring提供了一個方便的類 RegexpMethodPointcutAdvisor
,
它也允許我們引用一個通知(記住這里一個通知可以是攔截器,前置通知(before advice),異常通知(throws advice)等類型中的一個)。
在背后,如果使用J2SE 1.4或者以上版本,Spring將使用JdkRegexpMethodPointcut
,
在之前版本的虛擬機(jī)上,Spring將退回去使用Perl5RegexpMethodPointcut
。
可以通過設(shè)置perl5
屬性為true來強(qiáng)制使用Perl5RegexpMethodPointcut
。
使用RegexpMethodPointcutAdvisor
可以簡化織入,因?yàn)橐粋€bean可以同時作為切入點(diǎn)和通知器(advisor),如下所示:
<bean id="settersAndAbsquatulateAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="beanNameOfAopAllianceInterceptor"/> </property> <property name="patterns"> <list> <value>.*set.*</value> <value>.*absquatulate</value> </list> </property> </bean>
RegexpMethodPointcutAdvisor可以和任何通知類型一起使用
動態(tài)切入點(diǎn)比起靜態(tài)切入點(diǎn)在執(zhí)行時要消耗更多的資源。它們同時計(jì)算方法參數(shù)和靜態(tài)信息。 這意味著它們必須在每次方法調(diào)用時都被計(jì)算;由于參數(shù)的不同,結(jié)果不能被緩存。
動態(tài)切入點(diǎn)的主要例子是控制流
切入點(diǎn)。
Spring控制流切入點(diǎn)在概念上和AspectJ的cflow 切入點(diǎn)很相似,
雖然它的功能不如后者那么強(qiáng)大。(目前還不能讓一個切入點(diǎn)在另外一個切入點(diǎn)所匹配的連接點(diǎn)處執(zhí)行)。
一個控制流切入點(diǎn)匹配當(dāng)前的調(diào)用棧。例如,一個連接點(diǎn)被com.mycompany.web
包內(nèi)的一個方法或者SomeCaller
類調(diào)用,切入點(diǎn)就可能被激活。
控制流切入點(diǎn)是由org.springframework.aop.support.ControlFlowPointcut
類聲明的。
在運(yùn)行時控制流切入點(diǎn)的開銷是非常昂貴的,甚至與其它動態(tài)切入點(diǎn)比起來也是如此。在Java 1.4里, 它的開銷差不多是其它動態(tài)切入點(diǎn)的5倍。
Spring提供了很多有用的切入點(diǎn)超類來幫助你實(shí)現(xiàn)你自己的切入點(diǎn)。
因?yàn)殪o態(tài)切入點(diǎn)是最常用的,你可能會像下面那樣繼承StaticMethodMatcherPointcut。這只需要實(shí)現(xiàn)一個抽象方法 (雖然也可以覆蓋其它方法來定制行為):
class TestStaticPointcut extends StaticMethodMatcherPointcut { public boolean matches(Method m, Class targetClass) { // return true if custom criteria match } }
動態(tài)切入點(diǎn)也有很多超類。
你可以在使用自定義切入點(diǎn)的同時結(jié)合Spring 1.0 RC2和更高版本中的任意通知類型。