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