Spring中AOP,即面向切面编程,在使用AOP之前,先来了解一些AOP的术语。

一、术语

通知(Advice)
通知定义了切面是什么以及何时使用,以及何时执行这个切面。Spring AOP中有五种类型的通知:
1、前置通知(Before):在目标方法调用之前执行切面的相关方法;
2、后置通知(After):在目标方法完成之后调用切面,此时不关心目标方法的输出;
3、返回通知(AfterReturning):在目标方法成功执行之后调用;
4、异常通知(AfterThrowing):在目标方法抛出异常后调用通知;
5、环绕通知(Around):在目标方法执行之前和执行之后调用通知;

连接点(Join point)
连接点是程序执行过程中能够插入切面的一个点,可以是一个方法的执行,或者一个参数的处理。
Spring AOP中,Join point总是方法的执行点。

切点(Point cut)
切点定义了程序在何处使用切面,切点会匹配通知所要织入的一个或多个连接点。通常会使用明确的类和方法名称,或者使用正则表达式来匹配类或方法名称来指定切点。
Spring AOP在使用切点的正则表达式会用到如下标识符:
1、execution:匹配方法的连接点(最常用);
2、@annotation:匹配特定的连接点,其中连接点使用了特定的注解;
3、within:匹配特定类型的连接点(匹配连接点所在的Java类或者包);

切面(Aspect)
切面是切点和通知的结合,通知和切点共同定义切面的全部内容——它是什么,在何时、何处完成什么功能。可以简单的认为,使用 @Aspect注解的类都是切面。

引入(Introduction)

引入允许我们向现有的类添加新方法或属性,例如,我们可以创建一个Auditable通知类,该类记录了对象最后一次修改时的状态,这很简单,只需一个方法,setLastModified(Date),和一个实例变量来保存这个状态。然后这个新方法和实例变量就可以被引入到现有的类中,从而无需修改这些现有的类的情况下,让他们具有新的行为和状态。

以上摘自《Spring实战(第四版)》

织入(Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入:
1、编译器:切面在目标对象编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是以这种方式织入切面的。
2、类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入(LTW)就支持以这种方式织入切面。
3、运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态的创建一个代理对象。Spring AOP就是以这种方式织入切面的。

以上摘自《Spring实战(第四版)》

二、Spring Boot中使用AOP

1、Maven引入

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Example {
	
	String value() default "";
	
}

@Target作用:用于描述注解的使用范围,取值来源于ElementType 这个枚举类型。
@Retention作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。取值来源于RetentionPolicy 这个枚举类型。

3、定义切面

@Aspect
@Component
public class ExampleAspect {
	
	private static final Logger log = LoggerFactory.getLogger(ExampleAspect.class);
	
	@Pointcut("@annotation(com.example.annotation.Example)")
	public void pointcut() {
		
	}
	
	@Before("pointcut()")
	public void doBefore(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		Example test = method.getAnnotation(Example.class);
		log.info("Example注解内容为:" + test.value());
	}
	
	@AfterReturning(returning = "obj", pointcut = "pointcut()")
	public void doAfter(Object obj) {
		log.info("方法执行成功后返回结果为:" + obj.toString());
	}
	
}

4、测试类

@RestController
public class TestController {
	
	@Example("test")
	@GetMapping("{name}")
	public String get(@PathVariable("name") String name) {
		return name;
	}
	
}

启动项目,浏览器输入:localhost:8080/springboot,查看日志输出:

2019-05-11 10:16:28.883  INFO 3192 --- [nio-8080-exec-1] com.example.aspect.ExampleAspect         : Example注解内容为:test
2019-05-11 10:16:28.888  INFO 3192 --- [nio-8080-exec-1] com.example.aspect.ExampleAspect         : 方法执行成功后返回结果为:springboot