五種增強和部分AOP知識

本文內容:

1、前置增強

2、後置增強

3、返回增強

4、異常增強

5、環繞增強

6、Spring AOP支持如下三種通配符:

7、切入點表達式:

8、@Pointcut註解:

9、切面優先級:


1、前置增強

        @Before("execution(int mul (..))")
	public void before(JoinPoint joinPoint) {//前置增強;目標類的方法執行之前,會先執行此方法
		Object object = joinPoint.getTarget();
		Object [] args = joinPoint.getArgs();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":The "+name+" method begins.");
		System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
	}

    joinPoint.getTarget()獲取對象;(在本例中結果爲:com.jd.calculator.CalculatorService@2ac478)

    joinPoint.getArgs()獲取方法中傳入的參數值(在本例中是mul中傳入的參數值)

    joinPoint.getSignature().getName();獲取對應方法名稱(在本例中是mul)

    下面後置增強,返回增強,異常增強中的這些語句,意義也一樣。

2、後置增強

        @After("execution(int mul (int ,int))")
	public void after(JoinPoint joinPoint) {//後置增強;目標方法執行之後,執行此方法
		Object object = joinPoint.getTarget();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":The "+name+" method ends.");
	}

3、返回增強

        @AfterReturning(value="execution(int mul (..))",returning="result")
	public void afterReturning(JoinPoint joinPoint, Object result) {//返回增強;當執行目標類的方法拋出異常時,此方法不執行
		Object object = joinPoint.getTarget();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":Result of the "+name+" method:"+result);
	}

    在這裏,returning="result"中的result,要和afterReturning(JoinPoint joinPoint, Object result)中的result名稱一致,名稱可以隨便取,只需要保持一致就行。有一點需要注意:當目標類中的方法拋出異常時,返回增強不執行

4、異常增強

        @AfterThrowing(value="execution(int mul (..))",throwing="exception")
	public void afterThrowing(JoinPoint joinPoint,Exception exception) {//異常增強;Exception要和拋出的異常相同或者比它大,否則不會執行這個方法(比如,如果拋出異常是RuntimeException,那麼這裏的Exception改成RuntimeException也可以)
		Object object = joinPoint.getTarget();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":The "+name+" method have a exception:"+exception);
	}

    在這裏,throwing="exception"中的exception要和afterThrowing(JoinPoint joinPoint,Exception exception)中的exception名稱一致,同返回增強一樣,名稱可以隨意取,但是要保持一致。有一點需要注意:afterThrowing(JoinPoint joinPoint,Exception exception)中的Exception,要和目標類中方法拋出的異常相同,或者比目標類中方法拋出的異常大。

5、環繞增強

        @Around(value="execution(int * (..))")
	public Object around(ProceedingJoinPoint joinPoint) {
		Object result=null;
		Object [] args = joinPoint.getArgs();
		Object object = joinPoint.getTarget();
		String methodName = joinPoint.getSignature().getName();
		try {
			try {//前置增強
				System.out.println(object.getClass().getName()+":The "+methodName+" method begins.");
				System.out.println(object.getClass().getName()+":Parameters of the "+methodName+" method: ["+args[0]+","+args[1]+"]");
				result = joinPoint.proceed();//得到目標方法返回的值
			}finally {//後置增強
				System.out.println(object.getClass().getName()+":The "+methodName+" method ends.");
			}//返回增強
			System.out.println(object.getClass().getName()+":Result of the "+methodName+" method:"+result);
		} catch (Throwable exception) {//異常增強
			System.out.println(object.getClass().getName()+":The "+methodName+" method have a exception:"+exception.getMessage());
		}
		return result;
	}

    環繞增強同時實現了,以上四種增強。這裏面的args,object,和前置增強中的介紹一樣,methodName相當於前置增強中的name,result是目標類中方法執行後的返回值。

    在這裏可以解釋,前面四種增強同時執行後出現的一種現象:

輸出結果:
com.jd.calculator.CalculatorService:The mul method begins.
com.jd.calculator.CalculatorService:Parameters of the mul method: [1,1]
com.jd.calculator.CalculatorService:The mul method ends.
com.jd.calculator.CalculatorService:Result of the mul method:1
————>1

    現象是:由輸出結果可以發現,後置增強得到的結果,輸出順序在返回增強之前。

    代碼解釋:在環繞增強中,下面這段代碼相當於前置增強

System.out.println(object.getClass().getName()+":The "+methodName+" method begins.");
System.out.println(object.getClass().getName()+":Parameters of the "+methodName+" method: ["+args[0]+","+args[1]+"]");

    下面這段代碼相當於後置增強

System.out.println(object.getClass().getName()+":The "+methodName+" method ends.");

    下面這段代碼相當於返回增強

System.out.println(object.getClass().getName()+":Result of the "+methodName+" method:"+result);

    下面這段代碼相當於異常增強

System.out.println(object.getClass().getName()+":The "+methodName+" method have a exception:"+exception.getMessage());

    執行過程:在執行環繞增強中的代碼時,會先執行相當於前置增強的那段代碼,然後執行result = joinPoint.proceed();  得到了目標方法的返回值。此時如果目標方法中拋出異常的話,會執行finally中的相當於後置增強的代碼,接着就會執行catch中相當於異常增強的代碼,而相當於返回增強的那部分代碼不會執行。如果目標方法沒有拋出異常的話,按照順序執行,就會出現上面提到的現象:後置增強輸出結果順序在返回增強之前,而且無論目標類方法是否拋出異常,相當於後置增強的代碼都會執行。

6、Spring AOP支持如下三種通配符:

    1*:匹配任何數量字符,用於參數列表表示參數可以是任意數據類型,但是必須有參數,

    2..:方法中表示任意數量參數,在包中表示當前包及其子包,

    3+:匹配指定類型的子類型(不是子類);僅能作爲後綴放在類型模式後邊,(瞭解即可)

    前兩種通配符的使用,比如環繞增強中的註釋,@Around(value="execution(int * (..))"),可匹配目標類中的任意一個方法。

7、切入點表達式:

    execution:語法:execution([修飾符] 返回值類型 [包名.類名/接口名.]方法名([參數])[異常]),說明:a、該表達式用於指定匹配的方法;b、修飾符包括訪問權限和static、final以及synchronized;c、紅色中括號框起的部分可以省略。

    @annotation:

    語法:@annotation(註解全名),說明:該表達式用於匹配任意指定註解修飾的方法;

    例子:@annotation(org.springframework.transaction.annotation.Transactional):匹配org.springframework.transaction.annotation.Transactional修飾的任意方法;

8、@Pointcut註解:

    通過觀察發現CalculatorAspect類中@Before,@After,@AfterReturning、@AfterThrowing和@Around註解中切入點表達式相同,爲了簡化代碼,可以單獨自定義一個@Pointcut註解修飾的空方法,通過該方法可以簡化@Before,@After,@AfterReturning、@AfterThrowing和@Around註解中的切入點表達式,具體代碼如下:

@Aspect
@Component
public class BcalculatorTest {
	
	@Pointcut("execution(int * (..))")
	public void pointCut() {
		
	}

	@Before("pointCut()")
	public void before(JoinPoint joinPoint) {
		Object object = joinPoint.getTarget();
		Object [] args = joinPoint.getArgs();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":The "+name+" method begins.");
		System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
	}
}

    在這個例子中,@Before("pointCut()")中的pointCut()要與@Pointcut標記的方法名一致;@Pointcut()中的值,就是表達式的值

    如果需要增強的方法很多,而且表達式比較長時,需要修改表達式的時候,會非常耗費時間和精力。如果使用@PointCut註解,只需要修改@PointCut中表達式的值就可以,這樣會節省很多時間和精力。

9、切面優先級:

    如果一個方法匹配多個切面中相同類型增強方法,那麼必須明確指定各個切面優先級,否則切面優先級不確定,切面優先級的確定既可以通過在切面類添加@Order註解或實現Ordered接口實現,也可以在XML配置aop:aspect標籤的order屬性來實現。

    下面用例子來介紹:這裏有兩個類BcalculatorTest和AcalculatorTest,這兩個類中各有前置增強中的部分內容:

    BcalculatorTest.java

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BcalculatorTest {

	@Before("execution(int * (..))")
	public void before(JoinPoint joinPoint) {
		Object object = joinPoint.getTarget();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":The "+name+" method begins.");
	}
}

     AcalculatorTest.java

package com.jd.calculator;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AcalculatorTest {

	@Before("execution(int * (..))")
	public void before(JoinPoint joinPoint) {
		Object object = joinPoint.getTarget();
		Object [] args = joinPoint.getArgs();
		String name = joinPoint.getSignature().getName();
		System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
	}
}

    兩個類都有@Aspect註解;那麼問題來了,當程序執行時會先執行哪個類中的before方法呢?

    默認情況下:會先執行類名首字母在26個字母中靠前的那個類中的方法。這個例子執行結果爲:

com.jd.calculator.CalculatorService:Parameters of the mul method: [1,1]
com.jd.calculator.CalculatorService:The mul method begins.
————>1

    這和我們想要的結果不一致,下面使用@Order註解來實現我們想要的結果:我們設置先執行BcalculatorTest.java中的方法

@Aspect
@Component
@Order(2)//值越大,優先級越低,後執行
public class AcalculatorTest 




@Aspect
@Component
@Order(1)//值越小,優先級越高,先執行
public class BcalculatorTest 

    這時執行的結果爲:

com.jd.calculator.CalculatorService:The mul method begins.
com.jd.calculator.CalculatorService:Parameters of the mul method: [1,1]
————>1

 

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