1. 程式人生 > >spring_AOP前置通知&後置通知&返回通知&環繞通知

spring_AOP前置通知&後置通知&返回通知&環繞通知

Spring AOP

AspectJ:(Java社群裡最完整最流行的AOP框架)

spring自身也有一套AOP框架,但相比較於AspectJ,更推薦AspectJ

在Spring2.0以上版本中,可以使用基於AspectJ註解基於XML配置的AOP。

基於AspectJ註解:

用AspectJ註解宣告切面
要在Spring中宣告AspectJ切面,只需要在IOC容器中將切面宣告為Bean例項,當在Spring IOC容器中初始化AspectJ切面之後, Spring IOC容器就會為為那些與AspectJ切面相匹配的Bean建立代理。
AspectJ註解中,切面只是一個帶有@AspectJ註解的Java類

通知是標註有某種註解的的簡單的java方法 AspectJ支援5種類型的通知註解: [email protected] 前置通知,在目標方法執行之前執行
[email protected]:後置通知:在目標方法執行之後執行,無論是否發生異常
[email protected]:返回通知,在目標方法返回結果之後執行
[email protected]terThrowing:異常通知,在目標方法丟擲異常之後通知。
[email protected] 環繞通知,圍繞著目標方法執行。

基於AspectJ註解新增前置通知步驟:

1)加入jar包

com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar

commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

2)在配置檔案中加入aop和context的名稱空間

xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"

3)基於註解的方式

   1.在配置檔案里加入如下配置:

	<!-- 配置自動掃描的包  -->
	<context:component-scan base-package="com.wul.spring.aop.impl"></context:component-scan>
	
	<!-- 使AspectJ註解起作用:自動為匹配的類生成代理物件 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(當Spring IOC 容器偵測到Bean配置檔案中的<aop:aspectj-autoproxy>元素時,會自動與AspectJ切面匹配的Bean建立代理。)

  2.宣告一個切面的類並把橫切關注點程式碼抽象到切面的類中:

I.切面首先是一個IOC中的bean,即加入@Component註釋
II.切面還需要加入@Aspect

  3.在類中宣告各種通知

I.宣告一個方法

II.在方法前加入@Before註解

III.利用方法簽名編寫AspectJ切入點表示式

關於方法簽名編寫的切點表示式:

1.execution (*com.wul.spring.aop.impl.AtithmeticCalculator.*(..))    匹配 ArithmeticCalculator 中宣告的所有方法,    第一個 * 代表任意修飾符及任意返回值. 第二個 * 代表任意方法. .. 匹配任意數量的引數.     若目標類與介面與該切面在同一個包中, 可以省略包名. 2.execution (public * ArithmeticCalculator.*(..))    匹配 ArithmeticCalculator 介面的所有公有方法. 3.execution(* * , *()..)       一個 *代表匹配任意修飾符及任意返回值第二個 * 代表任意類的物件,第三個 *代表任意方法    引數列表中的.. 匹配任意數量的引數. 4.在 AspectJ 中, 切入點表示式可以通過操作符 &&, ||, ! 結合起來. 
5.可以在通知方法中宣告一個型別為 JoinPoint 的引數. 然後就能訪問連結細節. 如方法名稱和引數值.      public void beforeMethod(JoinPoint joinPoint)

     方法名:String methodName = joinPoint.getSignature().getName();
     引數值:List<Object> args = Arrays.asList(joinPoint.getArgs());

前置通知:

	//宣告該方法是一個前置通知:在目標方法開始之前執行
//	@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.add(int ,int))")
	@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
	public void beforeMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method "+methodName +" begins with "+ args);
	}
後置通知:
	//後置通知:在目標方法之後(無論是否發生異常),執行的通知,
	//在後置通知中還不能訪問目標方法執行的結果。執行結果須在返回通知中訪問。
	@After("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+" ends");
	
	}

返回通知:
	//返回通知:在目標方法正常結束執行後的通知
	//返回通知是可以訪問到目標方法的返回值的
	@AfterReturning(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
			        , returning = "result")
	public void afterRunningMethod(JoinPoint joinPoint , Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+" ends with the Result "+ result);
	}

異常通知:
	//在目標方法出現異常時會執行的程式碼,
	//可以訪問到異常物件,且可以指定在出現特定異常時在執行通知程式碼
	//如下面Exception ex,若是指定為NullpointerException ex就不會執行通知程式碼
	@AfterThrowing(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
			          , throwing="ex")
	public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+"occurs exception:"+ex);
	}
前置,後置,異常通知在動態代理類中實現的話:
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				String methodName = method.getName();
				//日誌
				System.out.println("The method "+methodName+" begin with "+Arrays.asList(args));
				//執行方法
				Object result = null;
				
				try{
					//前置通知
					 result = method.invoke(target, args);
					//返回通知,可以訪問到方法的返回通知
				}catch(Exception e){
					e.printStackTrace();
					//異常通知:可以訪問到方法出現的異常
				}
				
				//後置通知:因為方法可能出錯,所以訪問不到方法的返回值。
				
				//日誌
				System.out.println("The method "+methodName + " ends with "+result);
				return result;
			}



環繞通知:(類似於動態代理的過程)

package com.wul.spring.aop.impl;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//把這個類宣告為一個切面:需要把該類放入到IOC容器中,再宣告為一個切面
@Aspect
@Component
public class LogginAspect {
	
	//宣告該方法是一個前置通知:在目標方法開始之前執行
//	@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.add(int ,int))")
	@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
	public void beforeMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method "+methodName +" begins with "+ args);
	}
	
	//後置通知:在目標方法之後(無論是否發生異常),執行的通知,
	//在後置通知中還不能訪問目標方法執行的結果。執行結果須在返回通知中訪問。
	@After("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+" ends");
	
	}
	//返回通知:在目標方法正常結束執行後的通知
	//返回通知是可以訪問到目標方法的返回值的
	@AfterReturning(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
			        , returning = "result")
	public void afterRunningMethod(JoinPoint joinPoint , Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+" ends with the Result "+ result);
	}
	
	
	//在目標方法出現異常時會執行的程式碼,
	//可以訪問到異常物件,且可以指定在出現特定異常時在執行通知程式碼
	//如下面Exception ex,若是指定為NullpointerException ex就不會執行通知程式碼
	@AfterThrowing(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
			          , throwing="ex")
	public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+"occurs exception:"+ex);
	}
	
	//壞繞通知:需要攜帶ProceedingJoinPoint型別的引數
	//環繞通知類似於動態代理的全過程:ProceedingJoinPoint型別的引數可以決定是否執行目標方法
	//且環繞通知必須有返回值,返回值即目標方法的返回值。
	@Around("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))")
	public Object aroundMethod(ProceedingJoinPoint pjd){
		Object result = null;
		String methodName = pjd.getSignature().getName();
		Object args = Arrays.asList(pjd.getArgs());
		//執行目標方法
		try {
			//前置通知
			System.out.println("Arround:The method "+methodName +" begins with "+ args);			
			result = pjd.proceed();
			//後置通知
			System.out.println("Arround:The method "+ methodName+" ends");
		} catch (Throwable e) {
			e.printStackTrace();
			//異常通知
			System.out.println("Arround:The method "+ methodName+"occurs exception:"+e);
			//throw new RuntimeException(e);
			//不丟擲異常的話,異常就被上面抓住,執行下去,返回result,result值為null,轉換為int
		}
		//返回通知
		System.out.println("Arround:The method "+ methodName+" ends with the Result "+ result);
		
		//return 100;
		return result;
	}
	
}
下面給出示例:



AtithmeticCalculator.java

package com.wul.spring.aop.impl;

public interface AtithmeticCalculator {
	
		int add(int i, int j);
		int sub(int i, int j);
		int mul(int i, int j);
		int div(int i, int j);
		
}

AtithmeticCalculatorImpl.java

package com.wul.spring.aop.impl;

import org.springframework.stereotype.Component;

@Component
public class AtithmeticCalculatorImpl implements AtithmeticCalculator {

	@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;
	}

}

LogginAspect.java

package com.wul.spring.aop.impl;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//把這個類宣告為一個切面:需要把該類放入到IOC容器中,再宣告為一個切面
@Aspect
@Component
public class LogginAspect {
	
	//宣告該方法是一個前置通知:在目標方法開始之前執行
//	@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.add(int ,int))")
	@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
	public void beforeMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method "+methodName +" begins with "+ args);
	}
	
	//後置通知:在目標方法之後(無論是否發生異常),執行的通知,
	//在後置通知中還不能訪問目標方法執行的結果。執行結果須在返回通知中訪問。
	@After("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+" ends");
	
	}
	//返回通知:在目標方法正常結束執行後的通知
	//返回通知是可以訪問到目標方法的返回值的
	@AfterReturning(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
			        , returning = "result")
	public void afterRunningMethod(JoinPoint joinPoint , Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+" ends with the Result "+ result);
	}
	
	
	//在目標方法出現異常時會執行的程式碼,
	//可以訪問到異常物件,且可以指定在出現特定異常時在執行通知程式碼
	//如下面Exception ex,若是指定為NullpointerException ex就不會執行通知程式碼
	@AfterThrowing(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
			          , throwing="ex")
	public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method "+ methodName+"occurs exception:"+ex);
	}
	
	//壞繞通知:需要攜帶ProceedingJoinPoint型別的引數
	//環繞通知類似於動態代理的全過程:ProceedingJoinPoint型別的引數可以決定是否執行目標方法
	//且環繞通知必須有返回值,返回值即目標方法的返回值。
	@Around("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))")
	public Object aroundMethod(ProceedingJoinPoint pjd){
		Object result = null;
		String methodName = pjd.getSignature().getName();
		Object args = Arrays.asList(pjd.getArgs());
		//執行目標方法
		try {
			//前置通知
			System.out.println("Arround:The method "+methodName +" begins with "+ args);			
			result = pjd.proceed();
			//後置通知
			System.out.println("Arround:The method "+ methodName+" ends");
		} catch (Throwable e) {
			e.printStackTrace();
			//異常通知
			System.out.println("Arround:The method "+ methodName+"occurs exception:"+e);
			//throw new RuntimeException(e);
			//不丟擲異常的話,異常就被上面抓住,執行下去,返回result,result值為null,轉換為int
		}
		//返回通知
		System.out.println("Arround:The method "+ methodName+" ends with the Result "+ result);
		
		//return 100;
		return result;
	}
	
}

applicationContext.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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- 配置自動掃描的包  -->
	<context:component-scan base-package="com.wul.spring.aop.impl"></context:component-scan>
	
	<!-- 使AspectJ註解起作用:自動為匹配的類生成代理物件 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

Main.java

package com.wul.spring.aop.impl;

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的例項
		AtithmeticCalculator arithmeticCalculator = ctx.getBean(AtithmeticCalculator.class);
		
		//3.使用bean
		int result = arithmeticCalculator.add(3,6);
		System.out.println("result: "+result);
		
	    result = arithmeticCalculator.div(10,5);
		System.out.println("result: "+result);
		
		result = arithmeticCalculator.div(10,0);
	}	
	
}