天天看點

Spring注解驅動開發——AOP面向切面

AOP:在程式運作期間,動态的将某段代碼切入到指定方法運作時的指定時機運作,其實就是動态代理。

Spring提供了對AOP很好的支援,使用時需要導入spring-aspects包。

業務邏輯類:要求在業務方法運作時列印日志

public class MathCalculator {

	public int div(int i,int j){
		return i/j;
	}
}
           

切面類:類上需要注解@Aspect,切面類中的方法需要動态感覺業務方法的執行情況

@Aspect 
public class AopAspect {
	
	@Before("public int com.bdm.aop.MathCalculator.div(int, int)")
	public void logStart() {
		System.out.println("計算開始");
	}

	@After("public int com.bdm.aop.MathCalculator.*(int, int)")
	public void logEnd() {
		System.out.println("計算結束");
	}

	@AfterReturning("public int com.bdm.aop.MathCalculator.*(..)")
	public void logReturn() {
		System.out.println("計算結果");
	}

	@AfterThrowing("public int com.bdm.aop.MathCalculator.div(..)")
	public void logException() {
		System.out.println("計算異常");
	}
}
           

切面類中的方法也稱為通知方法:

    前置通知(@Before):在目标方法運作之前運作

    後置通知(@After):在目标方法運作之後運作,即使出現異常也會運作

    傳回通知(@AfterReturning):在目标方法正常傳回之後運作

    異常通知(@AfterThrowing):在目标方法運作出現異常之後運作

    環繞通知(@Around):動态代理,手動推進目标方法的運作

    在使用這些注解的時候必須配合切入點表達式,來指明在哪些方法執行之前、之後、傳回時、異常時執行,在寫切入點表達式時可以使用通配符*表示所有方法或者類,入參可以使用..表示所有參數類型

如果切入點表達式一樣的話,則可以使用一個方法抽取切入點表達式,注意該方法的名稱可以任意,而且方法體内無需寫任何内容,隻需要使用@PointCut注解并将切入點表達式标明即可:

@Aspect public class AopAspect {
	
	@Pointcut("execution(public int com.bdm.aop.MathCalculator.*(..))")
	public void pointCut(){}
	
	@Before("pointCut()")
	public void logStart() {
		System.out.println("計算開始");
	}

	@After("com.bdm.aop.AopAspect.pointCut()")
	public void logEnd() {
		System.out.println("計算結束");
	}

	@AfterReturning("pointCut()")
	public void logReturn() {
		System.out.println("計算結果");
	}

	@AfterThrowing("com.bdm.aop.AopAspect.pointCut()")
	public void logException() {
		System.out.println("計算異常");
	}
}
           

本類中使用時可直接寫方法名,其他類也想使用此切入點表達式時則需要使用該方法的全類名

容器會将标注有@Aspect注解的類認為是切面類

使用Spring的切面需要開啟Spring的切面自動代理,隻需要在配置類中加注解@EnableAspectJAutoProxy,Spring中有很多@EnableXxx注解,用來開啟一些功能

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

	@Bean
	public MathCalculator cal(){
		return new MathCalculator();
	}
	@Bean
	public AopAspect aspect(){
		return new AopAspect();
	}

}
           

配置bean怎麼區分哪個bean是切面類呢,它會看哪個類上有@Aspect注解,另外切面方法的執行僅對Spring容器中的bean起作用,對于我們自己new出來的對象是不起作用的,原因也很簡單,我們自己建立的bean并沒有被spring管理,也就沒有為其設定切面方法等。

通過JoinPoint對象擷取調用目标方法時的資訊,比如方法名、參數等,使用returning指定用通知方法的哪個入參接收傳回值,使用throwing指定用哪個入參接收異常,另外如果使用JoinPoint,則必須将其放在切面方法入參的第一個位置,否則會報錯:

@Aspect
public class AopAspect {
	
	@Pointcut("execution(public int com.bdm.aop.MathCalculator.*(..))")
	public void pointCut(){}
	
	@Before("pointCut()")
	public void logStart(JoinPoint point) {
		String name = point.getSignature().getName();
		Object[] args = point.getArgs();
		System.out.println(name + "方法計算開始,入參:" + Arrays.asList(args));
	}

	@After("com.bdm.aop.AopAspect.pointCut()")
	public void logEnd(JoinPoint point) {
		String name = point.getSignature().getName();
		System.out.println(name + "方法計算結束");
	}

	@AfterReturning(value="pointCut()",returning="obj")
	public void logReturn(JoinPoint point,Object obj) {
		String name = point.getSignature().getName();
		System.out.println(name +"方法的計算結果:" + obj.toString());
	}

	@AfterThrowing(value="com.bdm.aop.AopAspect.pointCut()",throwing="exception")
	public void logException(Exception exception) {
		System.out.println("計算異常:"+ exception.getMessage());
	}
}
           

總結:

1、将切面類(@Aspect标注的類)納入到配置類中,業務類對象也必須由Spring容器建立

2、切面方法加上通知注解(@Before、@After、@AfterReturning、@AfterThrowing、@Round),并寫上切入點表達式,告訴切面方法在執行哪些方法前、後等時點執行這些切面方法

3、啟用基于注解的AOP模式:配置類上加注解@EnableAspectJAutoProxy