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 AOPAOP完成办法,专门针对Spring Boot应用程序供给的一种简化装备和运用的办法。

  Spring AOPSpring结构供给的一种AOP完成办法。AOP是一种编程范式,而Spring AOPSpring结构对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";
    }
}

  运转并拜访:

AOP是什么?Spring Boot AOP 怎么使用?

AOP是什么?Spring Boot AOP 怎么使用?

(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表明连接点(即方针办法),它能够在盘绕告诉中被调用和操作。

代码的履行次序如下:

  1. 当连接点被触发时,即方针办法即将履行前,盘绕告诉doAround会被履行。
  2. 榜首行代码输出”开端履行盘绕告诉:”,表明盘绕告诉开端履行。
  3. joinPoint.proceed()调用表明持续履行方针办法,此行代码会触发方针办法的履行,并将方针办法的回来值存储在obj变量中。
  4. 第三行代码输出”结束盘绕告诉”,表明盘绕告诉的履行已经结束。
  5. 下一行的代码对方针办法的回来值进行修正,将其改为”do Around ” + obj,并将修正后的值赋给obj变量。
  6. 接着,输出修正后的obj的值。
  7. 最终,将修正后的obj回来作为方针办法的成果。

成果:

AOP是什么?Spring Boot AOP 怎么使用?

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

成果:

AOP是什么?Spring Boot AOP 怎么使用?

(4)回来告诉

//回来告诉
@AfterReturning("pointcut()")
public void AfterReturning(){
    System.out.println("履行回来告诉");
}

成果:

AOP是什么?Spring Boot AOP 怎么使用?

(5)反常告诉

//反常告诉
@AfterThrowing("pointcut()")
public void AfterThrowing(){
    System.out.println("履行反常告诉");
    // 能够在此处进行反常处理逻辑
}
@RequestMapping("/login")
public String login(){
    System.out.println("履行 UserController 的 login() 办法");
    throw new ArrayIndexOutOfBoundsException();
}

成果:

AOP是什么?Spring Boot AOP 怎么使用?

4. Spring AOP 完成原理

4.1 动态署理

  Spring AOP 是构建在动态署理基础上,因而 SpringAOP 的⽀持局限于⽅法级别的阻拦。

Spring AOP的扼要完成原理:

  1. 切入点表达式解析:Spring AOP首要解析界说的切入点表达式,该表达式指定了哪些办法将被织入切面逻辑。
  2. 切面逻辑界说:开发人员界说切面逻辑,包含要在方针办法履行前、履行后或抛出反常时履行的逻辑。
  3. 创立署理方针:Spring运用动态署理机制,在运转时动态地创立一个署理方针,该署理方针完成了方针方针所完成的接口。署理方针将会阻拦方针方针的办法调用。
  4. 办法阻拦:当客户端调用方针方针的办法时,实践上是调用署理方针的对应办法。署理方针会阻拦这个办法调用并履行切面逻辑。
  5. 履行切面逻辑:在署理方针的对应办法中,依据切入点表达式判断是否需求履行切面逻辑。假如需求,署理方针会调用切面逻辑的相关办法。
  6. 调用方针办法:在切面逻辑履行结束后,署理方针会持续调用方针方针的实践办法。

AOP是什么?Spring Boot AOP 怎么使用?

  织入(Weaving)是指将切面逻辑与方针类进行结合的进程。署理方针的生成机遇与织入机遇有关。依据织入机遇的不同,能够将织入分为以下三种办法:

  1. 编译期织入(Compile-Time Weaving):
    • 切面在方针类编译时被织入。
    • 需求特别的编译器来支撑,例如AspectJ的织入编译器。
    • 在方针类编译期间,切面逻辑被直接织入方针类的字节码中。
  2. 类加载期织入(Load-Time Weaving):
    • 切面在方针类加载到JVM时被织入。
    • 需求特别的类加载器(ClassLoader)来支撑。
    • 在方针类被引进应用之前,经过增强方针类的字节码来织入切面逻辑。
    • AspectJ5的加载时织入(Load-Time Weaving, LTW)支撑这种办法。
  3. 运转期织入(Runtime Weaving):
    • 切面在应用运转的某一时刻被织入。
    • 一般情况下,AOP容器会为方针方针动态创立署理方针。
    • 署理方针经过阻拦方针方针的办法调用,在适当的机遇履行切面逻辑。
    • Spring AOP便是以运转期织入的办法来织入切面。

  Spring AOP支撑两种类型的署理:JDK动态署理CGLIB署理。假如方针方针完成了InvocationHandler接口,Spring将运用JDK动态署理来创立署理方针。假如方针方针没有完成InvocationHandler接⼝,Spring将运用CGLIB署理,经过继承方针方针来创立署理方针。

它们首要区别:

  1. 接口要求:JDK动态署理要求方针方针完成接口,而CGLIB署理能够署理没有完成接口的类。
  2. 生成办法:JDK动态署理运用Java的反射机制生成署理方针,而CGLIB署理运用CGLIB库生成署理方针,经过修正方针类的字节码来完成。
  3. 署理方针类型:JDK动态署理生成的署理方针是完成了方针方针所完成的接口,而CGLIB署理生成的署理方针是方针方针的子类。