Spring AOP中 前置、後置、返回、異常、環繞通知的實例

1 在Spring中啓用AspectJ註解支持
  要在Spring應用中使用AspectJ註解,必須在classpath下包含AspectJ類庫:aopalliance.jar、aspectj.weaver.jar和spring-aspects.jar
  將aop Schema添加到<beans>根元素中。
  要在Spring IOC容器中啓用AspectJ註解支持,只要早bean配置文件中定義一個空的XML元素<aop:aspectj-autoproxy>
  當Spring IOC容器偵測到bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動爲與AspectJ切面匹配的bean創建代理
2 用AspectJ註解聲明切面
  要在Spring中聲明AspectJ切面,只需要在IOC容器中將切面聲明爲bean實例。當在Spring IOC容器中初始化AspectJ切面之後,Spring IOC容器就會爲那些與AspectJ切面相匹配的bean創建代理
  在AspectJ註解中,切面只是一個帶有@AspectJ註解的Java類
  通知是標註有某種註解的簡單的Java方法
  AspectJ支持5種類型的通知註解:
    @Before:前置通知,在方法執行之前返回
    @After:後置通知,在方法執行後執行
    @AfterRunning:返回通知,在方法返回結果之後執行
    @AfterThrowing:異常通知,在方法拋出異常之後
    @Around:環繞通知,圍繞着方法執行

下面我們來看一個實例:

ArithmeticCalculator接口:

package com.primary.spring.aop;

public interface ArithmeticCalculator {

	int add(int i, int j);
	int sub(int i, int j);
	int mul(int i, int j);
	int div(int i, int j);
}

ArithmeticCalculator的實現類 ArithmeticCalculatorImpl:

package com.primary.spring.aop;

import org.springframework.stereotype.Component;

@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public int add(int i, int j) {
		int result = i + j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result = i - j;
		return result;
	}

	@Override
	public int mul(int i, int j) {
		int result = i * j;
		return result;
	}

	@Override
	public int div(int i, int j) {
		int result = i / j;
		return result;
	}

}

配置自動掃描的包和配置自動爲匹配aspectJ 註解的java類生成代理對象的xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	
	<!-- 配置自動掃描的包 -->
	<context:component-scan base-package="com.primary.spring.aop"></context:component-scan>
	
	<!-- 配置自動爲匹配aspectJ 註解的java類生成代理對象 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

@Pointcut切點表達式 :後面的其他通知直接使用方法名來引用當前切入點的表達式

/**
	 * 切入點表達式的聲明,一般地,該方法中不需要再添加其他代碼
	 * 使用@Pointcut來聲明切入點表達式
	 * 後面的其他通知直接使用方法名來引用當前切入點的表達式
	 */
	@Pointcut("execution(* com.primary.spring.aop.*.*(..))")
	public void declareJointPointExpression() {}

@Before:前置通知,在方法執行之前返回

/**
	 *前置通知:聲明該方法是一個前置通知:在目標方法開始之前執行
	 * @param joinPoint
	 */
	@Before("declareJointPointExpression()")
	public void beforeMethod(JoinPoint joinPoint) {
		
		// 在通知的方法中聲明一個類型爲JoinPoint的參數, 然後就可以訪問鏈接細節,如方法和參數值。
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The methed " + methodName + " begins with " + args);
	}

 @After:後置通知,在方法執行後執行

/**
	 * 後置通知:聲明該方法是一個後置通知:在目標方法執行後(無論該方法是否發生異常), 一定會打印通知.
	 * 在後置通知中不能訪問目標方法執行的結果
	 * @param joinPoint
	 */
	@After("declareJointPointExpression()")
	public void AfterMethod(JoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The methed " + methodName + " ends");
	}

@AfterRunning:返回通知,在方法返回結果之後執行

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

@AfterThrowing:異常通知,在方法拋出異常之後

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

@Around:環繞通知,圍繞着方法執行:環繞通知類似於動態代理全過程即綜合了(前置、後置、返回、異常通知)爲一體的通知:


	/**
	 * 環繞通知:需要攜帶ProceedingJoinPoint 類型的參數
	 * 環繞通知類似於動態代理全過程即綜合了(前置、後置、返回、異常通知)爲一體的通知:ProceedingJoinPoint 類型的參數可以決定是否執行目標方法
	 * 且環繞通知必須要有返回值,返回值即爲目標方法的返回值
	 * @param proceedingJoinPoint
	 * @return 
	 */
	@Around("execution(* com.primary.spring.aop.*.*(..))")
	public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
		
		Object result = null;
		String methodName = proceedingJoinPoint.getSignature().getName();
		//執行目標方法
		try {
			//前置通知
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(proceedingJoinPoint.getArgs()));
			result = proceedingJoinPoint.proceed();
			//返回通知
			System.out.println("The method "+ methodName + " return with " + result);
		} catch (Throwable e) {
			e.printStackTrace();
			//異常通知
			System.out.println("The method "+ methodName + " occurs exception: " + e);
		}
		//後置通知
		System.out.println("The method " + methodName + " ends");
		
		return result;
	}

測試方法:

package com.primary.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		
		//1 .創建Spring的IOC容器
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

		//2. 從IOC容器中獲取bean的實例
		ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class);
		
		//3. 使用bean
		int result = arithmeticCalculator.add(3, 4);
		System.out.println("result: " + result);
		
		result = arithmeticCalculator.div(3, 1);
		System.out.println("result: " + result);
	}
}

我們發現用@Before、@After、@AfterRunning、@AfterThrowing執行的效果和單獨使用@Around通知效果一樣,但不等於環繞通知一定好於前面的四種通知:

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