天天看点

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

1. AOP的使用

1.1 环境准备

1.1.1 pom引入

要让sping的aop生效,需要先引入aop的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>
           

1.1.2 定义一个业务模块

package com.bjc.aop;

public class MathCalculator {
	public int div(int i,int j){
		System.out.println("MathCalculator...div...");
		return i/j;	
	}
}
           

1.1.3 定义一个日志切面类

package com.bjc.aop;

/**
 * 切面类
 */
public class LogAspects {
	
	public void logStart(){
		System.out.println("除法运行。。。参数列表是{}");
	}
	
	public void logEnd(){
		System.out.println("除法结束。。。");
	}
	
	public void logReturn(){
		System.out.println("除法正常返回。。。运行结果:{}");
	}
	
	public void logException(){
		System.out.println("除法异常。。。异常信息{}");
	}

}
           

1.2 通知方法

        给切面类的目标方法标注何时何地运行(通知注解)

1.2.1 前置通知——@Before

//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before"execution(public * com.bjc.aop.MathCalculator.*(..))"
public void logStart(){
	System.out.println("除法运行。。。参数列表是{}");
}
           

在logStart方法中可以加入参数JoinPoint,用于获取方法的相关信息,例如:

public void logStart(JoinPoint joinpoint){
	// 获取方法签名
	Signature signature = joinpoint.getSignature();
	// 获取调用的方法名称
	String methodname = signature.getName();
	
	// 获取参数列表
	Object[] args = joinpoint.getArgs();
	System.out.println("方法" + methodname + ",除法运行。。。参数列表是{  "+ Arrays.asList(args) +"  }");
}
           

 1.2.2 后置通知——@After

@After("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logEnd(){
	System.out.println("除法结束。。。");
}
           

loginEnd也可以加入参数JoinPoint

@After("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logEnd(JoinPoint joinpoint){
	System.out.println(joinpoint.getSignature().getName() + "除法结束。。。");
}
           

1.2.3 返回通知——@AfterReturning

@AfterReturning("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logReturn(){
	System.out.println("除法正常返回。。。运行结果:{}");
}
           

在logReturn中不但可以加入JoinPoint参数,还可以获取返回值

// returning的属性值与参数列表中的result对应
@AfterReturning("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logReturn(JoinPoint joinpoint,Object result){
	System.out.println(joinpoint.getSignature().getName() + "除法正常返回。。。运行结果:{"+ result +"}");
}
           

 1.2.4 异常通知——@AfterThrowing

@AfterThrowing("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logException(){
	System.out.println("除法异常。。。异常信息{}");
}
           

同样的,我们也可以获取异常信息

@AfterThrowing(value="execution(public * com.bjc.aop.MathCalculator.*(..)))",throwing = "e")
public void logException(JoinPoint joinpoint,Exception e){
	System.out.println("除法异常。。。异常信息{"+e+"}");
}
           

 注意:throwing的属性值e就是logexception的参数Exception的形参名。

1.2.5 环绕通知——@Around

动态代理,通过连接点(joinpoint.proced)手动推进目标方法运行

注意:通知的形参如果有多个的时候,JoinPoint必须为第一个参数,像返回值、异常,都要位于JoinPoint参数的后面。 

1.3 将切面类与业务类加入容器

@Configuration
public class ConfigOfAop {
	
	@Bean
	public MathCalculator mathCalculator() {
		return new MathCalculator();
	}
	
	@Bean
	public LogAspects logAspects() {
		return new LogAspects();
	}
}
           

1.4 指定切面类——@Aspect

        给切面类上加上注解@Aspect,spring就可以知道该类代表切面类。

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

1.5 开启基于注解的AOP模式——@EnableAspectJAutoProxy

在之前的基于配置的AOP中,我们使用的是如下配置

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
           

现在,我们没有这个配置文件,该怎么办了?我们可以在配置类中加上注解@EnableAspectJAutoProxy,来代替之前的配置

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

1.6 测试

public static void main(String[] args) {
	ApplicationContext context = new AnnotationConfigApplicationContext(ConfigOfAop.class);
	MathCalculator bean = context.getBean(MathCalculator.class);
	bean.div(10, 2);
}
           

运行结果:

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

将参数2改成0,运行结果:

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

使用try catch捕获异常,如图:

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

运行结果如下:

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

1.7 总结

         通过前面的案例,可以知道基于注解的AOP的步骤总结起来就三步:

1)将业务逻辑组件和切面类都加入到容器中,并告诉Spring哪个是切面类(@Aspect)

2)在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)

3)开启基于注解的AOP模式(@EnableAspectJAutoProxy)

2. 切入点表达式提取 ——@Pointcut

        我们发现,上例中,在每一个通知方法上都要写一个切入点表达式,比较繁琐,我们可以定义一个切入点方法然后通过注解@Pointcut将表达式抽取成公共的表达式,例如:

Spring注解开发——5. AOP1. AOP的使用2. 切入点表达式提取 ——@Pointcut

之后,将该方法应用到各个通知方法上,改进后的切面内如下:

package com.bjc.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 切面类
 */
@Aspect
public class LogAspects {
	//抽取公共的切入点表达式
	//1、本类引用:直接引用
	//2、其他的切面引用:使用全名  com.bjc.aop.LogAspects.cut1()
	@Pointcut("execution(public * com.bjc.aop.MathCalculator.*(..)))")
	public void cut1() {}
	
	//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
	@Before("cut1()")
	public void logStart(JoinPoint joinpoint){
		// 获取方法签名
		Signature signature = joinpoint.getSignature();
		// 获取调用的方法名称
		String methodname = signature.getName();
		
		// 获取参数列表
		Object[] args = joinpoint.getArgs();
		System.out.println("方法" + methodname + ",除法运行。。。参数列表是{  "+ Arrays.asList(args) +"  }");
	}
	
	@After("cut1()")
	public void logEnd(JoinPoint joinpoint){
		System.out.println(joinpoint.getSignature().getName() + "除法结束。。。");
	}
	
	// returning的属性值与参数列表中的result对应
	@AfterReturning(value="cut1()",returning = "result")
	public void logReturn(JoinPoint joinpoint,Object result){
		System.out.println(joinpoint.getSignature().getName() + "除法正常返回。。。运行结果:{"+ result +"}");
	}
	
	@AfterThrowing(value="cut1()",throwing = "e")
	public void logException(JoinPoint joinpoint,Exception e){
		System.out.println("除法异常。。。异常信息{"+e+"}");
	}

}
           

注意:如果想对aop下的所有的类进行切面注入,可以将MathCalculator去掉,即切入点表达式为:

@Pointcut("execution(public * com.bjc.aop..*(..)))")