1. Spring Boot AOP 是什么?
1.1 AOP 是什么?
AOP是面向切面编程(Aspect-Oriented Programming)的缩写。AOP是一种编程范式,旨在经过将横切关注点(cross-cutting concerns)从首要业务逻辑中分离出来,供给一种更好的代码模块化和可维护性,换句话说,便是对某一类事情的集中处理。
横切关注点指的是在应用程序中横跨多个模块或层的功用,例如日志记载、事务管理、安全性、缓存、反常处理等。
例如:在不运用AOP的情况下,每个Controller都要写一遍用户登录验证。当功用越来越多的时分,需求在每个功用里都写相同的代码,这就提高了代码的修正和维护的本钱。对于这种功用一致,且运用的地方较多的功用,就能够考虑AOP来一致处理了。
1.2 Spring Boot AOP 是什么?
Spring Boot AOP是依据Spring结构和Spring AOP的AOP完成办法,专门针对Spring Boot应用程序供给的一种简化装备和运用的办法。
Spring AOP是Spring结构供给的一种AOP完成办法。AOP是一种编程范式,而Spring AOP是Spring结构对AOP的详细完成。
2. AOP 的组成
AOP的组成有:切面、连接点、切点、告诉
2.1 切面(Aspect)
切面是横跨一个或多个类的模块化单元,它界说了与横切关注点相关的行为。切面由切点、告诉组成,它通常以类的方法表明。
2.2 切点(Pointcut)
切点(Pointcut)在面向切面编程(AOP)中起到了选择性阻拦和应用切面的效果,它能够被理解为一种规矩。
比方:有一个用户对别人的文章进行点评,这时分需求检测该用户是否登录,只要登录后才干点评。这便是切点,它相当于一种规矩。
2.3 告诉(Advice)
告诉是切面的一部分,它是在特定切点处履行的详细操作。切面由切点和告诉组成,切点用于界说在哪些连接点上应用告诉的规矩,而告诉界说了在这些连接点上履行的详细操作。在办法上增加相应的注解就表明相应的告诉:
- 前置告诉(@Before):在方针办法履行之前履行的告诉。能够在该告诉中进行一些准备工作或参数验证。
- 后置告诉(@After):在方针办法履行之后履行的告诉。能够在该告诉中进行一些清理工作或记载日志。
- 回来告诉(@AfterReturning):在方针办法成功履行并回来成果后履行的告诉。能够在该告诉中对办法的回来值进行处理或履行其他操作。
- 反常告诉(@AfterThrowing):在方针办法抛出反常后履行的告诉。能够在该告诉中处理反常或履行相应的反常处理逻辑。
- 盘绕告诉(@Around):在方针办法履行之前和之后都履行的告诉。它能够完全操控方针办法的履行进程,包含是否履行方针办法以及怎么处理回来值和反常。
2.4 连接点(Join Point)
连接点是指在应用程序履行进程中的特定点或事情,例如办法的调用、办法的履行、反常的抛出、属性的拜访等。**它是AOP中能够刺进切面逻辑的地方。**详细来说,连接点是在程序履行期间能够被阻拦的点。当程序运转到某个连接点时,AOP结构能够介入并履行相应的切面逻辑。
3. Spring Boot AOP 的演示
3.1 增加 Spring Boot AOP 依靠
增加如下的代码在 pom.xml文件中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.11</version>
</dependency>
3.2 界说切面与切点
切面一般是一个类,它里面有切点与告诉,下面是切点的界说办法:
@Aspect //切面
@Component //不能省掉,要在项目发动的时分发动
public class UserAOP {
//切点(装备阻拦规矩)
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){}
}
为什么是空办法?由于这个办法的效果是当一个标识。
@Pointcut注解里的表达式便是规矩,它的含义:
-
@Pointcut注解用于界说切点,即被切入的方位。 -
"execution(* com.example.demo.controller.UserController.*(..))"是切点表达式的内容。 -
execution()是切点指示符,表明匹配办法的履行。 -
*表明匹配恣意回来类型的办法。 -
com.example.demo.controller.UserController是方针类的全限定名,表明匹配该类。 -
*表明匹配类中的恣意办法名。 -
(..)表明匹配恣意参数的办法。
综合起来,这个切点表达式的意思是匹配 com.example.demo.controller.UserController 类中的一切办法,不管办法的回来类型和参数怎么。换言之,便是UserController中的一切办法都被阻拦了。
execution里的语法规矩:
execution(<修饰符><回来类型><包.类.办法(参数)><反常>)
其中,修饰符、反常部分能够省掉,其它的不能省掉。
-
*:匹配恣意字符,能够匹配零个或多个字符。在切点表达式中,*通配符能够用于匹配包、类或办法的称号中的恣意字符部分。 -
..:匹配恣意字符,能够匹配零个或多个字符、类或包路径。在切点表达式中,..通配符能够用于匹配类或包路径的恣意部分,例如com.example..表明匹配com.example包及其子包下的一切内容。 -
+:表明按照类型匹配指定类的一切子类。在切点表达式中,+通配符用于表明指定类的一切子类,包含该类自身。例如,com.example.demo.controller.UserController+表明匹配UserController类及其一切子类。
3.3 界说告诉
办法在被阻拦后需求做处理,处理便是告诉。
(1)前置告诉 + 后置告诉
@Aspect //切面
@Component //不能省掉,要在项目发动的时分发动
public class UserAOP {
//切点(装备阻拦规矩)
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){}
//前置告诉
@Before("pointcut()")
public void doBefore(){
System.out.println("履行前置告诉" + LocalDateTime.now());
}
//后置告诉
@After("pointcut()")
public void doAfter(){
System.out.println("履行后置告诉" + LocalDateTime.now());
}
}
@Before、@After等注解中的属性表明需求匹配的连接点(这里便是),以确认在哪些方位要应用切面的告诉。
下面是Controller的代码:
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/hi")
public String hi(){
System.out.println("履行 UserController 的 hi() 办法");
return "do user";
}
@RequestMapping("/login")
public String login(){
System.out.println("履行 UserController 的 login() 办法");
return "do login";
}
}
运转并拜访:
(2)盘绕告诉
@Aspect //切面
@Component //不能省掉,要在项目发动的时分发动
public class UserAOP {
//切点(装备阻拦规矩)
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){}
//盘绕告诉
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开端履行盘绕告诉:");
Object obj = joinPoint.proceed();
System.out.println("结束盘绕告诉");
//这里的 obj 便是 连接点办法的回来值,能够对其进行修正
obj = "do Around " + obj;
System.out.println(obj);
return obj;
}
}
在办法doAround中,参数ProceedingJoinPoint joinPoint表明连接点(即方针办法),它能够在盘绕告诉中被调用和操作。
代码的履行次序如下:
- 当连接点被触发时,即方针办法即将履行前,盘绕告诉
doAround会被履行。 - 榜首行代码输出”开端履行盘绕告诉:”,表明盘绕告诉开端履行。
-
joinPoint.proceed()调用表明持续履行方针办法,此行代码会触发方针办法的履行,并将方针办法的回来值存储在obj变量中。 - 第三行代码输出”结束盘绕告诉”,表明盘绕告诉的履行已经结束。
- 下一行的代码对方针办法的回来值进行修正,将其改为”do Around ” + obj,并将修正后的值赋给
obj变量。 - 接着,输出修正后的
obj的值。 - 最终,将修正后的
obj回来作为方针办法的成果。
成果:
(3)前置、后置告诉 + 盘绕告诉
@Aspect //切面
@Component //不能省掉,要在项目发动的时分发动
public class UserAOP {
//切点(装备阻拦规矩)
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){}
//前置告诉
@Before("pointcut()")
public void doBefore(){
System.out.println("履行前置告诉" + LocalDateTime.now());
}
//后置告诉
@After("pointcut()")
public void doAfter(){
System.out.println("履行后置告诉" + LocalDateTime.now());
}
//盘绕告诉
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开端履行盘绕告诉:");
Object obj = joinPoint.proceed();
System.out.println("结束盘绕告诉");
//这里的 obj 便是 连接点办法的回来值,能够对其进行修正
obj = "do Around " + obj;
System.out.println(obj);
return obj;
}
}
成果:
(4)回来告诉
//回来告诉
@AfterReturning("pointcut()")
public void AfterReturning(){
System.out.println("履行回来告诉");
}
成果:
(5)反常告诉
//反常告诉
@AfterThrowing("pointcut()")
public void AfterThrowing(){
System.out.println("履行反常告诉");
// 能够在此处进行反常处理逻辑
}
@RequestMapping("/login")
public String login(){
System.out.println("履行 UserController 的 login() 办法");
throw new ArrayIndexOutOfBoundsException();
}
成果:
4. Spring AOP 完成原理
4.1 动态署理
Spring AOP 是构建在动态署理基础上,因而 Spring 对 AOP 的⽀持局限于⽅法级别的阻拦。
Spring AOP的扼要完成原理:
- 切入点表达式解析:Spring AOP首要解析界说的切入点表达式,该表达式指定了哪些办法将被织入切面逻辑。
- 切面逻辑界说:开发人员界说切面逻辑,包含要在方针办法履行前、履行后或抛出反常时履行的逻辑。
- 创立署理方针:Spring运用动态署理机制,在运转时动态地创立一个署理方针,该署理方针完成了方针方针所完成的接口。署理方针将会阻拦方针方针的办法调用。
- 办法阻拦:当客户端调用方针方针的办法时,实践上是调用署理方针的对应办法。署理方针会阻拦这个办法调用并履行切面逻辑。
- 履行切面逻辑:在署理方针的对应办法中,依据切入点表达式判断是否需求履行切面逻辑。假如需求,署理方针会调用切面逻辑的相关办法。
- 调用方针办法:在切面逻辑履行结束后,署理方针会持续调用方针方针的实践办法。
织入(Weaving)是指将切面逻辑与方针类进行结合的进程。署理方针的生成机遇与织入机遇有关。依据织入机遇的不同,能够将织入分为以下三种办法:
- 编译期织入(Compile-Time Weaving):
- 切面在方针类编译时被织入。
- 需求特别的编译器来支撑,例如AspectJ的织入编译器。
- 在方针类编译期间,切面逻辑被直接织入方针类的字节码中。
- 类加载期织入(Load-Time Weaving):
- 切面在方针类加载到JVM时被织入。
- 需求特别的类加载器(ClassLoader)来支撑。
- 在方针类被引进应用之前,经过增强方针类的字节码来织入切面逻辑。
- AspectJ5的加载时织入(Load-Time Weaving, LTW)支撑这种办法。
- 运转期织入(Runtime Weaving):
- 切面在应用运转的某一时刻被织入。
- 一般情况下,AOP容器会为方针方针动态创立署理方针。
- 署理方针经过阻拦方针方针的办法调用,在适当的机遇履行切面逻辑。
- Spring AOP便是以运转期织入的办法来织入切面。
Spring AOP支撑两种类型的署理:JDK动态署理和CGLIB署理。假如方针方针完成了InvocationHandler接口,Spring将运用JDK动态署理来创立署理方针。假如方针方针没有完成InvocationHandler接⼝,Spring将运用CGLIB署理,经过继承方针方针来创立署理方针。
它们首要区别:
- 接口要求:JDK动态署理要求方针方针完成接口,而CGLIB署理能够署理没有完成接口的类。
- 生成办法:JDK动态署理运用Java的反射机制生成署理方针,而CGLIB署理运用CGLIB库生成署理方针,经过修正方针类的字节码来完成。
- 署理方针类型:JDK动态署理生成的署理方针是完成了方针方针所完成的接口,而CGLIB署理生成的署理方针是方针方针的子类。







