@Pointcut("execution(*transfer(..))")
private void anyOldTransfer() {}
切入點指定者的支持
Spring AOP 支持在切入點表達式中使用以下的AspectJ切入點指定者:
1:execution:匹配方法執行的鏈接點,這是你將會用到的Spring的最主要的切入點指定者。
2:within:限定匹配特定類型的鏈接點(在使用SpringAOP的時候,在匹配的類型中定義的方法的執行)。
3:this:限定匹配特定的鏈接點(使用Spring AOP的時候方法的執行),其中bean reference(Spring AOP 代理)是指定類型的實例。
4: target:限定匹配特定的鏈接點(使用SpringAOP的時候方法的執行),其中目標對象(被代理的appolication object)是指定類型的實例。
5: args:限定匹配特定的鏈接點(使用Spring AOP的時候方法的執行),其中參數是指定類型的實例。
6: @target:限定匹配特定的鏈接點(使用SpringAOP的時候方法的執行),其中執行的對象的類已經有指定類型的註解。
7: @args:限定匹配特定的鏈接點(使用SpringAOP的時候方法的執行),其中實際傳入參數的運行時類型有指定類型的註解。
8: @within:限定匹配特定的鏈接點,其中鏈接點所在類型已指定註解(在使用Spring AOP的時候,所執行的方法所在類型已指定註解)。
9: @annotation:限定匹配特定的鏈接點(使用SpringAOP的時候方法的執行),其中鏈接點的主題有某種給定的註解
合併切入點表達式
切入點表達式可使用‘&&', '||' 和'!'來合併.還能夠經過名字來指向切入點表達式。
切入點表達式的基本語法
Spring AOP 用戶可能會常用 execution pointcut designator。執行表達式的格式以下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
除了返回類型模式(上面代碼片段中的ret-type-pattern),名字模式和參數模式之外,全部的部分都是可選的。返回類型模式決定了方法的返回類型必須依次匹配一個鏈接點。
類型匹配模式
1:*:匹配任何數量字符;好比模式 (*,String) 匹配了一個接受兩個參數的方法,第一個能夠是任意類型,第二個則必須是String類型
2:..:匹配任何數量字符的重複,如在類型模式中匹配任何數量子包;而在方法參數模式中匹配任何數量參數,可使零到多個。
3: +:匹配指定類型的子類型;僅能做爲後綴放在類型模式後邊。
類型匹配模式示例
1:java.lang.String 匹配String類型;
2:java.*.String 匹配java包下的任何「一級子包」下的String類型;
如匹配java.lang.String,但不匹配java.lang.ss.String
3:java..* 匹配java包及任何子包下的任何類型;
如匹配java.lang.String、java.lang.annotation.Annotation
4:java.lang.*ing 匹配任何java.lang包下的以ing結尾的類型;
5:java.lang.Number+ 匹配java.lang包下的任何Number的子類型;
如匹配java.lang.Integer,也匹配java.math.BigInteger
切入點表達式的基本示例,使用execution
1:public * *(..)
任何公共方法的執行
2:* cn.javass..IPointcutService.*()
cn.javass包及全部子包下IPointcutService接口中的任何無參方法
3:* cn.javass..*.*(..)
cn.javass包及全部子包下任何類的任何方法
4:* cn.javass..IPointcutService.*(*)
cn.javass包及全部子包下IPointcutService接口的任何只有一個參數方法
5:* (!cn.javass..IPointcutService+).*(..)
非「cn.javass包及全部子包下IPointcutService接口及子類型」的任何方法
6:* cn.javass..IPointcutService+.*()
cn.javass包及全部子包下IPointcutService接口及子類型的的任何無參方法
7:* cn.javass..IPointcut*.test*(java.util.Date)
cn.javass包及全部子包下IPointcut前綴類型的的以test開頭的只有一個參數類型爲java.util.Date的方法,注意該匹配是根據方法簽名的參數類型進行匹配的,而不是根據執行時傳入的參數類型決定的如定義方法:public void test(Object obj);即便執行時傳入java.util.Date,也不會匹配的。
8:* cn.javass..IPointcut*.test*(..) throwsIllegalArgumentException, ArrayIndexOutOfBoundsException
cn.javass包及全部子包下IPointcut前綴類型的的任何方法,且拋出IllegalArgumentException和ArrayIndexOutOfBoundsException異常
9:* (cn.javass..IPointcutService+ &&java.io.Serializable+).*(..)
任何實現了cn.javass包及全部子包下IPointcutService接口和java.io.Serializable接口的類型的任何方法
10:@java.lang.Deprecated * *(..)
任何持有@java.lang.Deprecated註解的方法
11:@java.lang.Deprecated @cn.javass..Secure * *(..)
任何持有@java.lang.Deprecated和@cn.javass..Secure註解的方法
12:@(java.lang.Deprecated || cn.javass..Secure) * *(..)
任何持有@java.lang.Deprecated或@cn.javass..Secure註解的方法
13:(@cn.javass..Secure *) *(..)
任何返回值類型持有@cn.javass..Secure的方法
14:* (@cn.javass..Secure *).*(..)
任何定義方法的類型持有@cn.javass..Secure的方法
15:* *(@cn.javass..Secure (*) , @cn.javass..Secure (*))
任何簽名帶有兩個參數的方法,且這個兩個參數都被@ Secure標記了,如public void test(@Secure String str1, @Secure String str1);
16:* *((@ cn.javass..Secure *))或* *(@ cn.javass..Secure *)
任何帶有一個參數的方法,且該參數類型持有@ cn.javass..Secure;如public void test(Model model);且Model類上持有@Secure註解
17:* *(@cn.javass..Secure (@cn.javass..Secure *) ,@cn.javass..Secure (@cn.javass..Secure *))
任何帶有兩個參數的方法,且這兩個參數都被@ cn.javass..Secure標記了;且這兩個參數的類型上都持有@ cn.javass..Secure;
18:* *(java.util.Map<cn.javass..Model,cn.javass..Model>, ..)
任何帶有一個java.util.Map參數的方法,且該參數類型是以<cn.javass..Model, cn.javass..Model>爲泛型參數;注意只匹配第一個參數爲java.util.Map,不包括子類型;如public voidtest(HashMap<Model, Model> map, String str);將不匹配,必須使用「* *(java.util.HashMap<cn.javass..Model,cn.javass..Model>, ..)」進行匹配;而public void test(Map map, int i);也將不匹配,由於泛型參數不匹配
19:* *(java.util.Collection<@cn.javass..Secure*>)
任何帶有一個參數(類型爲java.util.Collection)的方法,且該參數類型是有一個泛型參數,該泛型參數類型上持有@cn.javass..Secure註解;如public voidtest(Collection<Model> collection);Model類型上持有@cn.javass..Secure
切入點表達式的基本示例,使用within匹配指定類型內的方法
1:within(cn.javass..*)
cn.javass包及子包下的任何方法執行
2:within(cn.javass..IPointcutService+)
cn.javass包或全部子包下IPointcutService類型及子類型的任何方法
3:within(@cn.javass..Secure *)
持有cn.javass..Secure註解的任何類型的任何方法必須是在目標對象上聲明這個註解,在接口上聲明的對它不起做用
切入點表達式的基本示例,使用this
使用「this(類型全限定名)」匹配當前AOP代理對象類型的執行方法;注意是AOP代理對象的類型匹配,這樣就可能包括引入接口方法也能夠匹配;注意this中使用的表達式必須是類型全限定名,不支持通配符
1:this(cn.javass.spring.chapter6.service.IPointcutService)
當前AOP對象實現了 IPointcutService接口的任何方法
2:this(cn.javass.spring.chapter6.service.IIntroductionService)
當前AOP對象實現了 IIntroductionService接口的任何方法也多是引入接口
切入點表達式的基本示例,使用target
使用「target(類型全限定名)」匹配當前目標對象類型的執行方法;注意是目標對象的類型匹配,這樣就不包括引入接口也類型匹配;注意target中使用的表達式必須是類型全限定名,不支持通配符
1:target(cn.javass.spring.chapter6.service.IPointcutService)
當前目標對象(非AOP對象)實現了IPointcutService接口的任何方法
2:target(cn.javass.spring.chapter6.service.IIntroductionService)
當前目標對象(非AOP對象) 實現了IIntroductionService接口的任何方法不多是引入接口
切入點表達式的基本示例,使用args
使用「args(參數類型列表)」匹配當前執行的方法傳入的參數爲指定類型的執行方法;注意是匹配傳入的參數類型,不是匹配方法簽名的參數類型;參數類型列表中的參數必須是類型全限定名,通配符不支持;args屬於動態切入點,這種切入點開銷很是大,非特殊狀況最好不要使用
1:args (java.io.Serializable,..)
任何一個以接受「傳入參數類型爲 java.io.Serializable」 開頭,且其後可跟任意個任意類型的參數的方法執行,args指定的參數類型是在運行時動態匹配的
切入點表達式的基本示例,使用@within
使用「@within(註解類型)」匹配因此持有指定註解類型內的方法;註解類型也必須是全限定類型名
1:@within cn.javass.spring.chapter6.Secure)
任何目標對象對應的類型持有Secure註解的類方法;必須是在目標對象上聲明這個註解,在接口上聲明的對它不起做用
切入點表達式的基本示例,使用@target
使用「@target(註解類型)」匹配當前目標對象類型的執行方法,其中目標對象持有指定的註解;註解類型也必須是全限定類型名
1:@target (cn.javass.spring.chapter6.Secure)
任何目標對象持有Secure註解的類方法;必須是在目標對象上聲明這個註解,在接口上聲明的對它不起做用
切入點表達式的基本示例,使用@args
使用「@args(註解列表)」匹配當前執行的方法傳入的參數持有指定註解的執行;註解類型也必須是全限定類型名
1:@args (cn.javass.spring.chapter6.Secure)
任何一個只接受一個參數的方法,且方法運行時傳入的參數持有註解cn.javass.spring.chapter6.Secure;動態切入點,相似於arg指示符;
切入點表達式的基本示例,使用@annotation
使用「@annotation(註解類型)」匹配當前執行方法持有指定註解的方法;註解類型也必須是全限定類型名
1:@annotation(cn.javass.spring.chapter6.Secure )
當前執行方法上持有註解 cn.javass.spring.chapter6.Secure將被匹配
切入點表達式的基本示例,使用bean
使用「bean(Bean id或名字通配符)」匹配特定名稱的Bean對象的執行方法;Spring AOP擴展的,在AspectJ中無相應概念
1:bean(*Service)
匹配全部以Service命名(id或name)結尾的Bean
切入點表達式的基本示例,使用reference pointcut
引用其餘命名切入點,只有@ApectJ風格支持,Schema風格不支持,以下所示:java
java代碼:
查看複製到剪貼板打印
@Pointcut(value="bean(*Service)") //命名切入點1
private void pointcut1(){}
@Pointcut(value="@args(cn.javass.spring.chapter6.Secure)") //命名切入點2
private void pointcut2(){}
@Before(value = "pointcut1() && pointcut2()") //引用命名切入點
public void referencePointcutTest1(JoinPoint jp) {
dump("pointcut1() && pointcut2()", jp);
}
聲明通知
通知是跟一個切入點表達式關聯起來的,而且在切入點匹配的方法執行以前或者以後或者以前和以後運行。 切入點表達式多是指向已命名的切入點的簡單引用或者是一個已經聲明過的切入點表達式。
前置通知(Before advice) ,使用 @Before註解聲明正則表達式
java代碼:
查看複製到剪貼板打印
@Aspect
public class BeforeExample {
@Before("execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
返回後通知(After returning advice) ,使用 @AfterReturning註解聲明
後通知(After (finally) advice) ,使用@After註解聲明
拋出後通知(After throwing advice) ,使用 @AfterThrowing註解聲明
能夠將拋出的異常綁定到通知的一個參數上 ,以下:算法
java代碼:
查看複製到剪貼板打印
@AfterThrowing(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions(Exception ex) {
// ...
}
環繞通知(Around Advice) ,使用 @Around註解聲明spring
java代碼:
查看複製到剪貼板打印
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
給Advice傳遞參數
一般狀況下,Advice都須要獲取必定的參數,好比:Before須要截獲傳入的參數,而After須要獲取方法的返回值等等。下面介紹兩種方式
方式一:使用JoinPoint來給Advice的方法傳遞參數
Spring AOP提供使用org.aspectj.lang.JoinPoint類型獲取鏈接點數據,任何通知方法的第一個參數均可以是JoinPoint(環繞通知是ProceedingJoinPoint,JoinPoint子類),固然第一個參數位置也能夠是JoinPoint.StaticPart類型,這個只返回鏈接點的靜態部分。
1:JoinPoint:提供訪問當前被通知方法的目標對象、代理對象、方法參數等數據express
java代碼:
查看複製到剪貼板打印
public interface JoinPoint {
String toString(); //鏈接點所在位置的相關信息
String toShortString(); //鏈接點所在位置的簡短相關信息
String toLongString(); //鏈接點所在位置的所有相關信息
Object getThis(); //返回AOP代理對象
Object getTarget(); //返回目標對象
Object[] getArgs(); //返回被通知方法參數列表
Signature getSignature(); //返回當前鏈接點簽名
SourceLocation getSourceLocation();//返回鏈接點方法所在類文件中的位置
String getKind(); //鏈接點類型
StaticPart getStaticPart(); //返回鏈接點靜態部分
}
2:ProceedingJoinPoint:用於環繞通知,使用proceed()方法來執行目標方法數組
java代碼:
查看複製到剪貼板打印
public interface ProceedingJoinPoint extends JoinPoint {
public Object proceed() throws Throwable;
public Object proceed(Object[] args) throws Throwable;
}
3:JoinPoint.StaticPart:提供訪問鏈接點的靜態部分,如被通知方法簽名、鏈接點類型等app
java代碼:
查看複製到剪貼板打印
public interface StaticPart {
Signature getSignature(); //返回當前鏈接點簽名
String getKind(); //鏈接點類型
int getId(); //惟一標識
String toString(); //鏈接點所在位置的相關信息
String toShortString(); //鏈接點所在位置的簡短相關信息
String toLongString(); //鏈接點所在位置的所有相關信息
}
4:示例:使用以下方式在通知方法上聲明,必須是在第一個參數,而後使用jp.getArgs()就能獲取到被通知方法參數:框架
java代碼:
查看複製到剪貼板打印
@Before(value="execution(* sayBefore(*))")
public void before(JoinPoint jp) {}
@Before(value="execution(* sayBefore(*))")
public void before(JoinPoint.StaticPart jp) {}
方式二:使用args來給Advice的方法傳遞參數,示例如:dom
java代碼:
查看複製到剪貼板打印
@Before(value="execution(* test(*)) && args(param)", argNames="param")
public void before1(String param) {
System.out.println("===param:" + param);
}
切入點表達式execution(* test(*)) && args(param) :
(1)首先execution(* test(*))匹配任何方法名爲test,且有一個任何類型的參數;
(2)args(param)將首先查找通知方法上同名的參數,並在方法執行時(運行時)匹配傳入的參數是使用該同名參數類型,即java.lang.String;若是匹配將把該被通知參數傳遞給通知方法上同名參數。
參數匹配的策略
1:能夠經過「argNames」屬性指定參數名 ,示例如:測試
java代碼:
查看複製到剪貼板打印
@Before(value=" args(param)", argNames="param") //明確指定了
public void before1(String param) {
System.out.println("===param:" + param);
}
2:若是第一個參數類型是JoinPoint、ProceedingJoinPoint或JoinPoint.StaticPart類型,應該從「argNames」屬性省略掉該參數名(可選,寫上也對),這些類型對象會自動傳入的,但必須做爲第一個參數。示例如:
java代碼:
查看複製到剪貼板打印
@Before(value=" args(param)", argNames="param") //明確指定了
public void before1(JoinPoint jp, String param) {
System.out.println("===param:" + param);
}
3:若是「 class文件中含有變量調試信息」,Spring將可使用這些方法簽名中的參數名來肯定參數名,示例以下:
java代碼:
查看複製到剪貼板打印
@Before(value=" args(param)") //不須要argNames了
public void before1(JoinPoint jp, String param) {
System.out.println("===param:" + param);
}
4:若是沒有「 class文件中含有變量調試信息」,將嘗試本身的參數匹配算法,若是發現參數綁定有二義性將拋出AmbiguousBindingException異常;對於只有一個綁定變量的切入點表達式,而通知方法只接受一個參數,說明綁定參數是明確的,從而能配對成功,示例以下:
@Before(value=" args(param)")
public void before1(JoinPoint jp, String param) {
System.out.println("===param:" + param);
}
5:以上策略失敗將拋出IllegalArgumentException。
處理參數
java代碼:
查看複製到剪貼板打印
@Around("execution(List<Account> find*(..)) &&" +
"com.xyz.SystemArchitecture.inDataAccessLayer() && " +"args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp, StringaccountHolderNamePattern)
throws Throwable {
String newPattern = preProcess(accountHolderNamePattern);
return pjp.proceed(new Object[] {newPattern});
}
獲取方法運行後的返回值
java代碼:
查看複製到剪貼板打印
@AfterReturning(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
Advice執行的順序
若是有多個通知想要在同一鏈接點運行會發生什麼?Spring AOP 的執行通知的順序跟AspectJ的同樣。 在「進入」鏈接點的狀況下,最高優先級的通知會先執行(因此上面給出的兩個前置通知(before advice)中,優先級高的那個會先執行)。 在「退出」鏈接點的狀況下,最高優先級的通知會最後執行。只須要記住通知是按照定義的順序來執行的就能夠了。
當定義在 不一樣的 切面裏的兩個通知都須要在一個相同的鏈接點中運行,那麼除非你指定,不然執行的順序是未知的。 你能夠經過指定優先級來控制執行順序。在Spring中能夠在切面類中實現org.springframework.core.Ordered 接口作到這一點。 在兩個切面中,Ordered.getValue()方法返回值較低的那個有更高的優先級。
java代碼:
查看複製到剪貼板打印
聲明一個切面 :<aop:aspect>
<aop:config>
<aop:aspect id="myAspect" ref="adviceBean">
...
</aop:aspect>
</aop:config>
<bean id="adviceBean" class="...">
...
</bean>
聲明一個切入點 :<aop:pointcut>
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>
在切面裏面聲明一個切入點和聲明一個頂級的切入點很是相似:
java代碼:
查看複製到剪貼板打印
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
當須要鏈接子表達式的時候,'&'在XML中用起來很是不方便,因此關鍵字'and', 'or' 和 'not'能夠分別用來代替'&', '||' 和 '!'。
java代碼:
查看複製到剪貼板打印
聲明通知Advice,Before Advice
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut="execution(* com.xyz.myapp.dao.*.*(..))"
method="doAccessCheck"/>
...
</aop:aspect>
返回後通知(After returning advice)
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
java代碼:
查看複製到剪貼板打印
拋出異常後通知(After throwing advice)
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
thowing="dataAccessEx"
method="doRecoveryActions"/>
...
</aop:aspect>
後通知(After (finally) advice)
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/>
...
</aop:aspect>
Around通知
java代碼:
查看複製到剪貼板打印
<aop:aspect id="aroundExample" ref="aBean">
<aop:around
pointcut-ref="businessService"
method="doBasicProfiling"/>
...
</aop:aspect>
通知的參數,能夠經過 arg-names 屬性來實現
java代碼:
查看複製到剪貼板打印
<aop:before
pointcut="com.xyz.lib.Pointcuts.anyPublicMethod()"
method="audit"
arg-names="auditable"/>
Advisor
「advisors」這個概念來自Spring1.2對AOP的支持,在AspectJ中是沒有等價的概念。 advisor就像一個小的自包含的切面,這個切面只有一個通知。
java代碼:
查看複製到剪貼板打印
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>
</aop:config>
Spring AOP仍是徹底用AspectJ?
優先選用Spring的AOP,由於它不是一我的在戰鬥,Spring除了AOP,還有IOC,還有事務等其餘功能。
若是你須要通知domain對象或其它沒有在Spring容器中管理的任意對象,那麼你須要使用AspectJ。
Spring AOP中使用@AspectJ仍是XML?
優先使用@AspectJ。
XML風格有兩個缺點。第一是它不能徹底將需求實現的地方封裝到一個位置。DRY原則中說系統中的每一項知識都必須具備單1、無歧義、權威的表示。當使用XML風格時,如何實現一個需求的知識被分割到支撐類的聲明中以及XML配置文件中。當使用@AspectJ風格時就只有一個單獨的模塊 -切面- 信息被封裝了起來。 第二是XML風格同@AspectJ風格所能表達的內容相比有更多的限制:僅僅支持"singleton"切面實例模型,而且不能在XML中組合命名鏈接點的聲明。
Spring AOP使用JDK動態代理或者CGLIB來爲目標對象建立代理。若是被代理的目標對象實現了至少一個接口,則會使用JDK動態代理。全部該目標類型實現的接口都將被代理。若該目標對象沒有實現任何接口,則建立一個CGLIB代理。
若是你但願強制使用CGLIB代理,(例如:但願代理目標對象的全部方法,而不僅是實現自接口的方法)那也能夠。可是須要考慮如下問題:
1:沒法通知(advise)Final 方法,由於他們不能被覆寫。
2:將CGLIB 2二進制發行包放在classpath下面,JDK自己就提供了動態代理
3:強制使用CGLIB代理須要將<aop:config> 的 proxy-target-class 屬性設爲true:
<aop:config proxy-target-class="true"></aop:config>
4:當須要使用CGLIB代理和@AspectJ自動代理支持,請按照以下的方式設置 <aop:aspectj-autoproxy> 的proxy-target-class 屬性:
<aop:aspectj-autoproxy proxy-target-class="true"/>
切入點的API
org.springframework.aop.Pointcut 是最核心的接口,用來將 通知應用於特定的類和方法,完整的接口定義以下:
java代碼:
查看複製到剪貼板打印
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
1:將Pointcut接口分割成有利於重用類和方法匹配的兩部分,以及進行更細粒度的操做組合(例如與另外一個方法匹配實現進行「或操做」)。
2:ClassFilter接口用來將切入點限定在一個給定的類集合中。若是matches()方法老是返回true,全部目標類都將被匹配:
public interface ClassFilter {
boolean matches(Class clazz);
}
3:MethodMatcher接口一般更重要,完整的接口定義以下:
java代碼:
查看複製到剪貼板打印
public interface MethodMatcher {
boolean matches(Method m, Class targetClass);
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}
matches(Method, Class)方法用來測試這個切入點是否匹配目標類的指定方法。這將在AOP代理被建立的時候執行,這樣能夠避免在每次方法調用的時候都執行。若是matches(Method, Class )對於一個給定的方法返回true,而且isRuntime() 也返回true,那麼matches(Method, Class , Object[])將在每一個方法調用的時候被調用。這使得切入點在通知將被執行前能夠查看傳入到方法的參數。
大多數MethodMatcher是靜態的,這意味着isRuntime()方法返回 false。在這種狀況下,matches(Method, Class ,Object[])永遠不會被調用。
靜態切入點
靜態切入點基於方法和目標類進行切入點判斷而不考慮方法的參數。在多數狀況下,靜態切入點是高效的、最好的選擇。Spring只在第一次調用方法時執行靜態切入點:之後每次調用這個方法時就不須要再執行。
1:正則表達式切入點
正則表達式是最經常使用的描述靜態切入點的方式,多數AOP框架都支持這種方式。org.springframework.aop.support.Perl5RegexpMethodPointcut是一個最基本的正則表達式切入點,它使用Perl 5正則表達式語法。示例以下:
java代碼:
查看複製到剪貼板打印
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.Perl5RegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
攔截around通知
Spring裏使用方法攔截的around通知兼容AOP聯盟接口。實現around通知的MethodInterceptor應當實現下面的接口:
java代碼:
查看複製到剪貼板打印
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
提示:在invok方法裏面不要忘記調用:invocation.proceed();
前置通知
java代碼:
查看複製到剪貼板打印
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
後置通知
java代碼:
查看複製到剪貼板打印
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)throws Throwable;
}
異常通知
若是鏈接點拋出異常,異常通知(throws advice)將在鏈接點返回後被調用。 Spring提供類型檢查的異常通知,這意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法:它只是一個標記接口用來標識所給對象實現了一個或者多個針對特定類型的異常通知方法。這些方法應當知足下面的格式
afterThrowing([Method], [args], [target], subclassOfThrowable)
只有最後一個參數是必須的。所以異常通知方法對方法及參數的需求,方法的簽名將從一到四個參數之間變化。 下面是一些throws通知的例子。
當一個RemoteException(包括它的子類)被拋出時,下面的通知會被調用:
java代碼:
查看複製到剪貼板打印
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
引入通知
java代碼: 查看複製到剪貼板打印 public interface IntroductionInterceptor extends MethodInterceptor { boolean implementsInterface(Class intf); } 使用ProxyFactoryBean建立AOP代理 使用ProxyFactoryBean或者其它IoC相關類帶來的最重要的好處之一就是建立AOP代理,這意味着通知和切入點也能夠由IoC來管理。這是一個強大的功能並使得某些特定的解決方案成爲可能 下面描述ProxyFactoryBean的屬性: 1:proxyTargetClass:這個屬性爲true時,目標類自己被代理而不是目標類的接口。若是這個屬性值被設爲true,CGLIB代理將被建立 2:optimize:用來控制經過CGLIB建立的代理是否使用激進的優化策略。除非徹底瞭解AOP代理如何處理優化,不然不推薦用戶使用這個設置。目前這個屬性僅用於CGLIB代理;對於JDK動態代理(缺省代理)無效。 3:frozen:用來控制代理工廠被配置以後,是否還容許修改通知。缺省值爲false(即在代理被配置以後,不容許修改代理的配置)。 4:exposeProxy:決定當前代理是否被保存在一個ThreadLocal中以便被目標對象訪問。(目標對象自己能夠經過MethodInvocation來訪問,所以不須要ThreadLocal。)若是個目標對象須要獲取代理並且exposeProxy屬性被設爲true,目標對象可使用AopContext.currentProxy()方法。 5:aopProxyFactory:使用AopProxyFactory的實現。這提供了一種方法來自定義是否使用動態代理,CGLIB或其它代理策略。 缺省實現將根據狀況選擇動態代理或者CGLIB。通常狀況下應該沒有使用這個屬性的須要;它是被設計來在Spring 1.1中添加新的代理類型的。 6:proxyInterfaces:須要代理的接口名的字符串數組。若是沒有提供,將爲目標類使用一個CGLIB代理 7:interceptorNames:Advisor的字符串數組,能夠包括攔截器或其它通知的名字。順序是很重要的,排在前面的將被優先服務。就是說列表裏的第一個攔截器將可以第一個攔截調用。 這裏的名字是當前工廠中bean的名字,包括父工廠中bean的名字。這裏你不能使用bean的引用由於這會致使ProxyFactoryBean忽略通知的單例設置。 你能夠把一個攔截器的名字加上一個星號做爲後綴(*)。這將致使這個應用程序裏全部名字以星號以前部分開頭的advisor都被應用。