目录
一、AOP概念
面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,旨在提高代码的模块化和封装性。在AOP中,程序被分解成核心业务逻辑和横切关注点(cross-cutting concerns)。这些横切关注点是那些在各个模块中被广泛使用的代码,比如日志、安全性、事务管理等。
AOP通过将这些横切关注点从核心业务逻辑中分离出来,实现了模块化的开发方式。AOP的主要概念是切面(Aspect),切面是一种横切关注点的模块化实现方式,可以在不同模块中重复使用,提高了代码的重用性和可维护性。
总的来说,面向切面编程是一种提高代码模块化和封装性的编程范式,通过将横切关注点从核心业务逻辑中分离出来,实现了更加灵活和可维护的代码结构。一般来说,AOP切面编程都是非业务代码,比如打印日志。否则,业务不一样,就很难抽离出来变成公共的。
1. 切面
切面是横切关注点的模块化实现。它可以包含多个“通知”(Advice)和“切入点”(Pointcut),定义了在哪里以及如何实现横切逻辑。
假设有四个方法,需要在每个方法中都输出一个日志,传统发放就需要在每个方法中写一条输出语句,如果有一万个方法,就需要将该输出语句写一万次,那这种方法显然是不可行的。所以需要将这条打印语句抽离封装好,需要用的的方法调用封装好的打印语句就好了。
上图所示,将长的椭圆形看成一个个方法,然后在方法上横切一刀,每个椭圆形就会有个横切面,这个横切面就是我们需要抽取出来的业务。我们把全部的横切面都集成一个大的横切面,就类似于将公共模块封装,需要用的时候直接调用即可。也就是说,把上面的四个横切面整合成一个切面对象,这就是面向切面编程。
不使用AOP, 就需要在每个方法中都添加一个打印语句
public class UserService {
public void addUser(User user) {
// 日志输出
System.out.println("正在处理...");
// 核心业务逻辑
}
public void delUser(Integer id) {
// 日志输出
System.out.println("正在处理...");
// 核心业务逻辑
}
public void updateUser(User user) {
// 日志输出
System.out.println("正在处理...");
// 核心业务逻辑
}
public void queryAllUser() {
// 日志输出
System.out.println("正在处理...");
// 核心业务逻辑
}
}
使用AOP
添加AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1. 将日志输出的代码抽取出来
public class UserService {
public void addUser(User user) {
// 核心业务逻辑
}
public void delUser(Integer id) {
// 核心业务逻辑
}
public void updateUser(User user) {
// 核心业务逻辑
}
public void queryAllUser() {
// 核心业务逻辑
}
}
2. 定义一个切面类(logAspect)
@Aspect // 定义一个切面类
@Component // 标记该类为 spring 容器的 Bean,把这个切面类交给容器管理
@Order(Ordered.LOWEST_PRECEDENCE -1) // 定义加载 spring 容器中 Bean 加载顺序的,值越小优先级越高
public class LoggingAspect {
// 定义一个切入点,匹配 UserService 的所有方法
// @Pointcut 注解用于指定哪些方法需要被拦截并执行增强处理(例如,日志记录、权限检查等)
// @execution() 方法用于匹配哪些方法需要增强
// 第一个 * 表示 匹配任何返回值
// 第二个 * 表示 匹配所有方法
// .. 表示 匹配任何参数,无论有无参数都匹配
@Pointcut("execution(* com.example.UserService.*(..))")
public void userServiceMethods() { }
// 前置通知:在方法执行前记录日志
@Before("userServiceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("正在处理...");
}
}
在这个例子里,LoggingAspect
就是一个切面,它的前置通知在 UserService
的方法执行前自动插入日志记录逻辑。这样,你的业务代码就干净了,不再需要手动写日志逻辑。
2. 通知
通知定义了切面的具体行为,也就是在何时、何地、以何种方式对目标方法进行增强。根据增强发生的时机,通知分为五种:前置通知、后置通知、返回通知、异常通知、环绕通知。
前置通知(Before Advice):在方法调用前执行。
后置通知(After Advice):在方法调用后执行。
返回通知(After Returning Advice):在方法成功返回后执行。
异常通知(After Throwing Advice):在方法抛出异常后执行。
环绕通知(Around Advice):在方法执行的前后都执行,可以完全控制方法的执行过程。
3. 切入点
切入点(Pointcut)是用来定义你想要拦截哪些方法,即哪些方法会被增强。配合"通知" 一起工作,通知决定了在方法执行前、执行后或抛出异常时执行什么逻辑,即拦截后要做什么事情。
4. 连接点
连接点可以理解为业务逻辑层中的方法。程序执行当中的一个方法调用、一个异常抛出都可以作为连接点。
5. 织入
织入是将切面与目标对象(业务代码)结合的过程。在 Spring AOP 中,织入通常发生在运行时&