1. 程式人生 > >Spring AOP返回通知&異常通知&環繞通知(二十二)

Spring AOP返回通知&異常通知&環繞通知(二十二)

一、返回通知

  • 無論連線點是正常返回還是丟擲異常, 後置通知都會執行. 如果只想在連線點返回的時候記錄日誌, 應使用返回通知代替後置通知.

  • 在方法法正常結束受執行的程式碼;

  • 返回通知是可以訪問到方法的返回值的!

  • 在返回通知中, 只要將 returning 屬性新增到 @AfterReturning 註解中, 就可以訪問連線點的返回值. 該屬性的值即為用來傳入返回值的引數名稱.

  • 必須在通知方法的簽名中新增一個同名引數. 在執行時, Spring AOP 會通過這個引數傳遞返回值.

  • 原始的切點表示式需要出現在 pointcut 屬性中。

(1)程式碼實現

/**
 * 在方法法正常結束受執行的程式碼
 * 返回通知是可以訪問到方法的返回值的!
 */
@AfterReturning(value="declareJointPointExpression()",
		returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("The method " + methodName + " ends with " + result);
}

(2)測試

public class AopTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArithmeticCalculator calculator = context.getBean(ArithmeticCalculator.class);

        System.out.println("result : " + calculator.div(10, 1));
    }
}

結果 

(3)異常測試 

public class AopTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArithmeticCalculator calculator = context.getBean(ArithmeticCalculator.class);

        System.out.println("result : " + calculator.div(10, 0));
    }
}

丟擲異常時,返回通知沒有執行 

二、異常通知

  • 只在連線點丟擲異常時才執行異常通知

  • throwing 屬性新增到 @AfterThrowing 註解中, 也可以訪問連線點丟擲的異常. Throwable 是所有錯誤和異常類的超類. 所以在異常通知方法可以捕獲到任何錯誤和異常.

  • 如果只對某種特殊的異常型別感興趣, 可以將引數宣告為其他異常的引數型別. 然後通知就只在丟擲這個型別及其子類的異常時才被執行.

(1)異常通知程式碼實現

/**
 * 在目標方法出現異常時會執行的程式碼.
 * 可以訪問到異常物件; 且可以指定在出現特定異常時在執行通知程式碼
 */
@AfterThrowing(value="declareJointPointExpression()",
		throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("The method " + methodName + " occurs excetion:" + e);
}

(2)測試

public class AopTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArithmeticCalculator calculator = context.getBean(ArithmeticCalculator.class);

        System.out.println("result : " + calculator.div(10, 0));
    }
}

結果 

(3)正常程式沒有異常測試 

public class AopTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArithmeticCalculator calculator = context.getBean(ArithmeticCalculator.class);

        System.out.println("result : " + calculator.div(10, 1));
    }
}

程式沒有丟擲異常時,異常通知沒有執行 

三、環繞通知

  • 環繞通知是所有通知型別中功能最為強大的, 能夠全面地控制連線點. 甚至可以控制是否執行連線點.

  • 對於環繞通知來說, 連線點的引數型別必須是 ProceedingJoinPoint . 它是 JoinPoint 的子介面, 允許控制何時執行, 是否執行連線點.

  • 在環繞通知中需要明確呼叫 ProceedingJoinPoint proceed() 方法來執行被代理的方法. 如果忘記這樣做就會導致通知被執行了, 但目標方法沒有被執行.

  • 注意: 環繞通知的方法需要返回目標方法執行之後的結果, 即呼叫 joinPoint.proceed(); 的返回值, 否則會出現空指標異常。

(1)環繞通知程式碼實現

/**
 * 環繞通知需要攜帶 ProceedingJoinPoint 型別的引數.
 * 環繞通知類似於動態代理的全過程: ProceedingJoinPoint 型別的引數可以決定是否執行目標方法.
 * 且環繞通知必須有返回值, 返回值即為目標方法的返回值
 */
@Around("execution(public int aop.ArithmeticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){

	Object result = null;
	String methodName = pjd.getSignature().getName();

	try {
		//前置通知
		System.out.println("The method 前置通知 " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
		//執行目標方法
		result = pjd.proceed();
		//返回通知
		System.out.println("The method 返回通知 " + methodName + " ends with " + result);
	} catch (Throwable e) {
		//異常通知
		System.out.println("The method 異常通知 " + methodName + " occurs exception:" + e);
		throw new RuntimeException(e);
	}
	//後置通知
	System.out.println("The method 後置通知 " + methodName + " ends");

	return result;
}

(2)測試

public class AopTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArithmeticCalculator calculator = context.getBean(ArithmeticCalculator.class);

        System.out.println("result : " + calculator.div(10, 1));
    }
}

結果 

(3)異常程式測試 

public class AopTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArithmeticCalculator calculator = context.getBean(ArithmeticCalculator.class);

        System.out.println("result : " + calculator.div(10, 0));
    }
}

程式丟擲異常時,後置通知和返回通知沒有執行 ,前置通知和異常通知正常執行。

總結 

AOP通知和普通java程式的對應關係,如下圖所示: