前言: spring 的環繞通知和前置通知,後置通知有著很大的區別,主要有兩個重要的區別: 1) 目標方法的調用由環繞通知決定,即你可以決定是否調用目標方法,而前置和後置通知 是不能決定的,他們只是在方法的調用前後執行通知而已,即目標方法肯定是要執行的。 2) 環繞通知可以控制返回對象,即你可以返回 ...
前言:
spring 的環繞通知和前置通知,後置通知有著很大的區別,主要有兩個重要的區別:
1) 目標方法的調用由環繞通知決定,即你可以決定是否調用目標方法,而前置和後置通知 是不能決定的,他們只是在方法的調用前後執行通知而已,即目標方法肯定是要執行的。
2) 環繞通知可以控制返回對象,即你可以返回一個與目標對象完全不同的返回值,雖然這很危險,但是你卻可以辦到。而後置方法是無法辦到的,因為他是在目標方法返回值後調用
這裡是經過我自己測試的過的例子,使用面向切麵來處理一些問公共的問題,比如,許可權管理,事務的委托
下麵的例子就是使用環繞通知,當程式發生異常時,重覆提交請求,重覆的次數是可以設定的
當我們開發企業級應用時,通常會想要從幾個切麵來引用模塊化的應用和特定操作的集合,下麵是一個典型的通用切麵,看起來可能像下麵這樣(這也是Spring文檔里的)
package test.prefer.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SystemArchitecture {
/**
* A join point is in the web layer if the method is defined
* in a type in the com.xyz.someapp.web package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.web..*)")
public void inWebLayer() {}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.someapp.service package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.service..*)")
public void inServiceLayer(){}
/**
* A join point is in the data access layer if the method is defined
* in a type in the com.xyz.someapp.dao package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.dao..*)")
public void inDataAccessLayer(){}
/**
* A business service is the execution of any method defined on a service
* interface. This definition assumes that interfaces are placed in the
* "service" package, and that implementation types are in sub-packages.
*
* If you group service interfaces by functional area (for example,
* in packages com.xyz.someapp.abc.service and com.xyz.def.service) then
* the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
* could be used instead.
*
* Alternatively, you can write the expression using the 'bean'
* PCD, like so "bean(*Service)". (This assumes that you have
* named your Spring service beans in a consistent fashion.)
*/
@Pointcut("execution(* test.prefer.aspect.*.*(..))")
public void businessService(){}
/**
* A data access operation is the execution of any method defined on a
* dao interface. This definition assumes that interfaces are placed in the
* "dao" package, and that implementation types are in sub-packages.
*/
@Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
public void dataAccessOperation(){}
}
一、定義自己的一個切麵
/*
*文件名:ConcurrentOperationExecutor.Java
*描述:<描述>
*修改人:Administrator
*/
package test.prefer.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
/**
* @author
*@date 2010-6-1
*/
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder(){
return this.order;
}
public void setOrder(int order){
this.order = order;
}
@Around("test.prefer.aspect.SystemArchitecture.businessService()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
//環繞通知處理方法
int numAttempts = 0;
Exception lockFailureException;
do {
numAttempts++;
try {
System.out.println("環繞通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............");
return pjp.proceed();
}
catch(Exception ex) {
lockFailureException = ex;
}
}
while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}
說明:
請註意切麵實現了 Ordered
介面,這樣我們就可以把切麵的優先順序設定為高於事務通知 (我們每次重試的時候都想要在一個全新的事務中進行)。maxRetries
和order
屬性都可以在Spring中配置。主要的動作在doConcurrentOperation
這個環繞通知方法中發生。 請註意這個時候我們所有的businessService()
方法都會使用這個重試策略。 我們首先會嘗試處理,如果得到一個Exception
異常, 我們僅僅重試直到耗盡所有預設的重試次數(spring開發文檔)
二、在配置文件里配置這個切麵
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor"
class="test.prefer.aspect.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
好了,下麵我們就試一下效果吧
三、測試效果
1)新建一個測試的bean: MyTestAspect,代碼如下:
package test.prefer.aspect;
/**
* 這是一個切麵類
*/
import org.aspectj.lang.annotation.Aspect;
public class MyTestAspect {
int k=0;
public void test(String args) throws Exception{
System.out.println("這裡是[ 目標 ]方法test()"+ ++k);
if(k<2){
throw new Exception();
}
}
}
這個類必須在連接點的包或者子包下麵,
在SystemArchitecture里有定義
@Pointcut("execution(* test.prefer.aspect.*.*(..))")
public void businessService(){}
2)applicationContext.xml里配置 MyTestAspect
<bean id="test" class="test.prefer.aspect.MyTestAspect"/>
3)好了,看看效果吧
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.prefer.aspect.MyTestAspect;
public class example {
public static void main(String args[]){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
MyTestAspect t =(MyTestAspect)ctx.getBean("test");
try{
t.test("");
}catch(Exception e){
System.out.println("main()中處理異常"+e);
}
}
}
輸出結果是:
環繞通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............
這裡是[ 目標 ]方法test()1
環繞通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............
這裡是[ 目標 ]方法test()