6.3.1. 聲明一個切面
有了schema的支持,切面就和常規的Java對象一樣被定義成application context中的一個bean。 對象的字段和方法提供了狀態和行為信息,XML文件則提供了切入點和通知信息。
切面使用<aop:aspect>來聲明,backing bean(支持bean)通過 ref 屬性來引用:
<aop:config> <aop:aspect id="myAspect" ref="aBean">... </aop:aspect></aop:config><bean id="aBean" class="..."> ...</bean>
切面的支持bean(上例中的"aBean")可以象其他Spring bean一樣被容器管理配置以及依賴注入。
切入點可以在切面里面聲明,這種情況下切入點只在切面內部可見。切入點也可以直接在<aop:config>下定義,這樣就可以使多個切面和通知器共享該切入點。
一個描述service層中表示所有service執行的切入點可以如下定義:
<aop:config> <aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..))"/></aop:config>
注意切入點表達式本身使用了 Section 6.2, “@AspectJ支持” 中描述的AspectJ 切入點表達式語言。 如果你在Java 5環境下使用基於schema的聲明風格,可參考切入點表達式類型中定義的命名式切入點,不過這在JDK1.4及以下版本中是不被支持的(因為依賴於 Java 5中的AspectJ反射API)。 所以在JDK 1.5中,上面的切入點的另外一種定義形式如下:
<aop:config> <aop:pointcut id="businessService"expression="com.xyz.myapp.SystemArchitecture.businessService()"/></aop:config>
假定你有 Section 6.2.3.3, “共享常見的切入點(pointcut)定義”中說描述的 SystemArchitecture 切面。
在切面里面聲明一個切入點和聲明一個頂級的切入點非常類似:
<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'可以分別用來代替'&', '||' 和 '!'。
注意這種方式定義的切入點通過XML id來查找,並且不能定義切入點參數。在基於schema的定義風格中命名切入點支持較之@AspectJ風格受到了很多的限制。
和@AspectJ風格一樣,基於schema的風格也支持5種通知類型並且兩者具有同樣的語義。
Before通知在匹配方法執行前進入。在<aop:aspect>里面使用<aop:before>元素進行聲明。
<aop:aspect id="beforeExample" ref="aBean"><aop:before pointcut-ref="dataAccessOperation" method="doAccessCheck"/>...</aop:aspect>
這里 dataAccessOperation 是一個頂級(<aop:config>)切入點的id。 要定義內置切入點,可將 pointcut-ref 屬性替換為 pointcut 屬性:
<aop:aspect id="beforeExample" ref="aBean"><aop:before pointcut="execution(* com.xyz.myapp.dao.*.*(..))" method="doAccessCheck"/>...</aop:aspect>
我們已經在@AspectJ風格章節中討論過了,使用命名切入點能夠明顯的提高代碼的可讀性。
Method屬性標識了提供了通知的主體的方法(doAccessCheck)。這個方法必須定義在包含通知的切面元素所引用的bean中。 在一個數據訪問操作執行之前(執行連接點和切入點表達式匹配),切面中的"doAccessCheck"會被調用。
After returning通知在匹配的方法完全執行后運行。和Before通知一樣,可以在<aop:aspect>里面聲明。例如:
<aop:aspect id="afterReturningExample" ref="aBean"><aop:after-returning pointcut-ref="dataAccessOperation" method="doAccessCheck"/>...</aop:aspect>
和@AspectJ風格一樣,通知主體可以接收返回值。使用returning屬性來指定接收返回值的參數名:
<aop:aspect id="afterReturningExample" ref="aBean"><aop:after-returning pointcut-ref="dataAccessOperation" returning="retVal" method="doAccessCheck"/>...</aop:aspect>
doAccessCheck方法必須聲明一個名字叫 retVal 的參數。 參數的類型強制匹配,和先前我們在@AfterReturning中講到的一樣。例如,方法簽名可以這樣聲明:
public void doAccessCheck(Object retVal) {...
After throwing通知在匹配方法拋出異常退出時執行。在 <aop:aspect> 中使用after-throwing元素來聲明:
<aop:aspect id="afterThrowingExample" ref="aBean"><aop:after-throwing pointcut-ref="dataAccessOperation" method="doRecoveryActions"/>...</aop:aspect>
和@AspectJ風格一樣,可以從通知體中獲取拋出的異常。 使用throwing屬性來指定異常的名稱,用這個名稱來獲取異常:
<aop:aspect id="afterThrowingExample" ref="aBean"><aop:after-throwing pointcut-ref="dataAccessOperation" thowing="dataAccessEx" method="doRecoveryActions"/>...</aop:aspect>
doRecoveryActions方法必須聲明一個名字為 dataAccessEx 的參數。 參數的類型強制匹配,和先前我們在@AfterThrowing中講到的一樣。例如:方法簽名可以如下這般聲明:
public void doRecoveryActions(DataAccessException dataAccessEx) {...
After (finally)通知在匹配方法退出后執行。使用 after 元素來聲明:
<aop:aspect id="afterFinallyExample" ref="aBean"><aop:after pointcut-ref="dataAccessOperation" method="doReleaseLock"/>...</aop:aspect>
Around通知是最后一種通知類型。Around通知在匹配方法運行期的“周圍”執行。 它有機會在目標方法的前面和后面執行,並決定什么時候運行,怎么運行,甚至是否運行。 Around通知經常在需要在一個方法執行前或后共享狀態信息,並且是線程安全的情況下使用(啟動和停止一個計時器就是一個例子)。 注意選擇能滿足你需求的最簡單的通知類型(i.e.如果簡單的before通知就能做的事情絕對不要使用around通知)。
Around通知使用 aop:around 元素來聲明。 通知方法的第一個參數的類型必須是 ProceedingJoinPoint 類型。 在通知的主體中,調用 ProceedingJoinPoint的proceed() 方法來執行真正的方法。 proceed 方法也可能會被調用並且傳入一個 Object[] 對象 - 該數組將作為方法執行時候的參數。 參見 Section 6.2.4.5, “環繞通知(Around Advice)” 中提到的一些注意點。
<aop:aspect id="aroundExample" ref="aBean"><aop:around pointcut-ref="businessService" method="doBasicProfiling"/>...</aop:aspect>
doBasicProfiling 通知的實現和@AspectJ中的例子完全一樣(當然要去掉注解):
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal;}
<aop:aspect id= " aop_aspect_frisetclass " ref= " AopAdvice ">
<!-- 定義個After增強處理,直接指定切入點表達式,以切面 Bean 中的 Release() 方法作為增強處理方法 -->
<aop:around pointcut= " execution(* cn.damai.std.mypoxy.FrisetClass.*(..)) " method= " doBasicProfiling " />
<aop:before pointcut= " execution(* cn.damai.std.mypoxy.FrisetClass.addStudent(..)) " method= " aopbeforemethod "/>
<aop:after pointcut= " execution(* cn.damai.std.mypoxy.FrisetClass.addStudent(..)) " method= " aopaftermethod "/>
<aop:after-returning pointcut= " execution(* cn.damai.std.mypoxy.FrisetClass.getStudentScores(..)) " returning= " val " method= " aopafterreturnmethod "/>
<aop:after-throwing pointcut= " execution(* cn.damai.std.mypoxy.FrisetClass.getStudentScores(..)) " throwing= " exdate " method= " aopafterthrowmethod "/>
</aop:aspect>
</aop:config>
// start stopwatch
Object retVal = pjp.proceed();
if(pjp.getArgs()!= null){
for(Object o : pjp.getArgs()){
logger.debug( " 參賽= "+o);
}
}
// stop stopwatch
return retVal;
}
public void aopbeforemethod(){
logger.debug( " 執行 方法 aopbeforemethod ");
}
public void aopaftermethod(){
}
public void aopafterreturnmethod(Object val){
logger.debug( " 執行 方法 aopafterreturnmethod 返回值 "+val);
}
public void aopafterthrowmethod(Throwable exdate){
logger.debug( " 執行 方法 aopafterthrowmethod "+exdate);
}