[轉載] spring aop 環繞通知around和其他通知的區別

一名強者發表於2018-08-02

前言:

     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 介面,這樣我們就可以把切面的優先順序設定為高於事務通知 (我們每次重試的時候都想要在一個全新的事務中進行)。maxRetriesorder 屬性都可以在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()

相關文章