spring aop總結

Spring AOP

Spring AOP術語以及概念

  • Join Point: 程序執行中的一個點。該次執行可以是方法調用、異常處理、類初始化或者對象初始化。Spring AOP只支持方法調用。如果還需要使用其他加入點,可以使用Spring和AspectJ結合使用。
  • Advice:在加入點需要具體執行的。具體的advice類型有@Before,@After,@Around,@AfterThrowing以及@AfterReturning。
  • Pointcut:一個advice必須執行的一個加入點的集合。一個advice不必應用於所有的加入點(joint point),因此切點(pointcut)很好的控制了advice在程序組件的執行。Spring中切點使用表達式,以及支持AspectJ的切點表試語言。
  • Aspect:advice以及切點的結合決定了在程序中的邏輯以及應該在哪裏執行。Aspect使用常規類的註解@Aspect實現,該註解可以獲得Spring AspectJ的支持。

定義切點

切點定義了advice被應用的執行的點,以下是切點支持的標識符:

標識符 描述
execution 限制匹配方法執行的加入點
within 限制匹配僅某個類型中的加入點,例如:within(com.packt.ch3.TransferService).
args 限制匹配指定類型參數的加入點,例如:args(account,..).
this 限制匹配bean引用或者spring代理對象是指定類型的實例的加入點,例如:this(com.packt.ch3.TransferService).
target 限制匹配給定目標對象是給定類型的一個實例的加入點,例如:target(com.packt.ch3.TransferService).
@within 限制匹配申明的類型有給定的註解,例如:@within(org.springframework.transaction.annotation.Transactional).
@target 限制匹配目標對象有給定類型的註解,例如:@target(org.springframework.transaction.annotion.Transactional).
@args 限制匹配實際的傳入參數有給定類型的註解,例如: @args(com.packt.ch3.Lockable).
@annotation 限制匹配執行的方法有給定的註解,例如:@annotation(org.springframework.transaction.annotation.Transactinal).

execution描述符的切點表達式:
* 使用execution():推薦使用方法匹配,以下是匹配的模式(其中[]表示是可選填的):

[Modifiers] ReturnType [ClassType] MethodName ([Arguments]) [throws ExceptionType]
* 可以通過加入其它的切點創建聚合切點,可以使用&&, ||, !操作符連接(這些操作符分別表示AND,OR,以及NOT)。

execution示例:
execution(* com.packt.ch3.bankingapp.service.AccountService.findAccountById(..))

advice類型

  • @Before:該advice在加入點位置之前執行,並且在切面中使用@Before註解。示例代碼如下:
@Pointcut("execution(*com.packt.ch03.bankingapp.service.TransferService.transfer())")
public void transfer(){}

@Before("transfer()")
public void beforeTransfer(JoinPoint joinPoint) {
 LOGGER.info("validate account balance before transferring amount");
}

注意:如果@Before方法拋出異常,目標方法tranfer不會被調用
* @After: 該advice方法在加入點方法正常退出或者沒有拋出異常返回時執行的。示例代碼:

@Pointcut("execution(*com.packt.ch03.bankingapp.service.TransferService.transfer())")
public void transfer(){}

@After("transfer()")
public void afterTransfer(JoinPoint joinPoint) {
 LOGGER.info("Successfully transferred from source account to dest account");
}
  • @AfterReturning:如果只想匹配一個方法正常返回時,可以使用該註解,例如:
@Pointcut("execution(*com.packt.ch03.bankingapp.service.TransferService.transfer())")
public void transfer(){}

@AfterReturning(pointcut="transfer() and args(source, dest, amount)",returning="isTransferSuccessful")
public void afterTransferReturns(Joinpoint joinPoint, Acount source, Account dest, Double amount, boolean isTransferSuccessful) {
  if (isTransferSuccessful) {
    LOGGER.info("Amount transferred successfully ");
    //find remaining balance of source account
    }
}
  • @AfterThrowing:匹配方法拋出異常時調用此advice。這對於記錄方法調用異常信息很有幫助,示例代碼:
@Pointcut("execution(*com.packt.ch03.bankingapp.service.TransferService.transfer())")
public void transfer(){}

@AfterThrowing(pointcut="transfer()", throwing="minimumAmountException")
public void exceptionFromTransfer(JoinPoint joinPoint, MinimumAmountException minimumAmoutException) {
    LOGGER.info("Exception thrown from transfer method: " + minimumAmountException.getMessage());
}

類似的throwing指定的異常類型必須匹配才能調用。
* @Around:該註解是@Before與@After的結合,但是比其更加強大,能夠完全控制方法的執行。添加@Around advice的方法的第一個參數應該是ProceedingJoinPoint。代碼示例:

@Pointcut("execution(*com.packt.ch03.bankingapp.service.TransferService.transfer())")
public void transfer(){}

@Around("transfer()")
public boolean aroundTransfer(ProceedingJoinPoint proceedingJoinPoint){
    LOGGER.info("Inside Around advice, before calling transfer method ");
    boolean isTransferSuccessful = false;
    try {
        isTransferSuccessful = (Boolean) proceedingJointPoint.proceed();
    } catch (Throwable e) {
        LOGGER.error(e.getMessage(), e);
    }
    LOGGER.info("Inside Around advice, after returning from transfer method"):
    return isTransferSuccessful;
    }

在@Around advice註解的方法中,可以調用proceed方法一次,多次甚至是不調用,能夠完全控制

切面初始化模型

默認的,聲明的切面(aspect)是單例(singleton),因此每個類加載器(class loader)(不是每個虛擬機)的切面對象只有一個實例。切面實例在類加載器作爲垃圾回收時會銷燬。
如果想使切面相對於類實例擁有私有屬性,那麼切面需要時有狀態的。爲此,Spring使用AspectJ支持提供的perthis以及pertarget初始化模型。AspectJ是一個獨立的類庫,除了有perthis,pertarget外,還有percflow,percflowbelow,pertypewithin等,不過Spring集成的AspectJ不支持這些方式。
使用perthis創建一個有狀態的切面對象,示例如下:

  @Aspect("perthis(com.packt.ch03.bankingapp.service.TransferService)")
  public class TransferAspect {
    //Add your per instance attributes holding private data
    //Define your advice methods
  }

一旦我們@Aspect使用perthis語句,對於每個執行transfer方法(每個唯一的對象在切點匹配表達式的加入點傾向於使用this)的單獨TransferService對象會創建一個切面實例。該切面對象在TransferService對象失效時失效。
pertarget與perthis相似,在pertarget中,加入點在匹配切點表達式時每個唯一的目標對象會創建一個切面實例。
Spring使用代理模式,通過創建代理對象編織(weave)切面(aspect)於目標對象。

To be continued.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章