AOP简介
- AOP(Aspect Oriented Programming) :面向切面编程,可简单理解为面向特定方法编程。
- 场景:部分业务方法运行较慢,需要统计每一个业务方法的执行耗时。
AOP优势
- 减少重复代码:通过切面统一处理横切关注点,减少业务代码中的重复逻辑。
- 代码无侵入:切面代码与业务代码分离,业务代码无需关心横切关注点的实现。
- 提高开发效率:快速实现横切关注点,如日志、事务管理等。
- 维护方便:横切关注点集中管理,便于维护和升级。
Spring AOP开发步骤
- 引入AOP依赖:在
pom.xml中引入spring-boot-starter-aop依赖。 - 编写AOP程序:定义切面类,使用
@Aspect注解,编写通知方法,并使用切入点表达式匹配目标方法。
AOP核心概念
- 连接点(JoinPoint) :可以被AOP控制的方法,暗含方法执行时的相关信息。
- 通知(Advice) :指那些重复的逻辑,即共性功能,最终体现为一个方法。
- 切入点(PointCut) :匹配连接点的条件,通知仅会在切入点方法执行时被应用。
- 切面(Aspect) :描述通知与切入点的对应关系(通知+切入点)。
- 目标对象(Target) :通知所应用的对象。
AOP进阶
通知类型
- @Around:环绕通知,在目标方法前、后都被执行,需要自己调用
ProceedingJoinPoint.proceed()来让原始方法执行。 - @Before:前置通知,在目标方法前被执行。
- @After:后置通知,在目标方法后被执行,无论是否有异常都会执行。
- @AfterReturning:返回后通知,在目标方法返回后被执行,有异常不会执行。
- @AfterThrowing:异常后通知,在目标方法发生异常后执行。
切入点表达式
-
execution:根据方法的签名来匹配,语法为
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)。*:单个独立的任意符号。..:多个连续的任意符号。
-
@annotation:根据注解匹配,用于匹配标识有特定注解的方法。
通知顺序
-
当有多个切面的切入点都匹配到了目标方法时,多个通知方法都会被执行。
-
执行顺序:
- 不同切面类中,默认按照切面类的类名字母排序。
- 目标方法前的通知方法:字母排名靠前的先执行。
- 目标方法后的通知方法:字母排名靠前的后执行。
-
可以使用
@Order(数字)加在切面类上来控制顺序,数字小的先执行(目标方法前)或后执行(目标方法后)。
连接点信息
- 在Spring中用
JoinPoint抽象了连接点,可以获取方法执行时的相关信息,如目标类名、方法名、方法参数等。 - 对于
@Around通知,获取连接点信息只能使用ProceedingJoinPoint。 - 对于其他四种通知,获取连接点信息只能使用
JoinPoint。
ThreadLocal应用
-
ThreadLocal:线程的局部变量,为每个线程提供单独一份存储空间,具有线程隔离的效果。
-
应用场景:在同一个线程/同一个请求中,进行数据共享,如存储当前登录员工ID。
-
操作步骤:
- 定义
ThreadLocal操作的工具类。 - 在
TokenFilter中解析完当前登录员工ID,将其存入ThreadLocal。 - 在AOP程序中,从
ThreadLocal中获取当前登录员工的ID。
- 定义
AOP案例
案例需求
- 将案例中增、删、改相关接口的操作日志记录到数据库表中。
- 日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长。
实现步骤
- 定义日志实体类和Mapper接口:用于存储操作日志信息。
- 定义切面类:使用
@Around环绕通知,匹配增、删、改接口方法。 - 在切面类中获取当前登录员工ID:通过
ThreadLocal获取。 - 记录日志信息:在切面类中编写逻辑,将日志信息保存到数据库。
切入点表达式
- 使用
execution表达式匹配增、删、改接口方法,如:
javaCopy Code
@Around("execution(* com.itheima.controller.*.save(..)) || " +
"execution(* com.itheima.controller.*.delete(..)) || " +
"execution(* com.itheima.controller.*.update(..))")
```
- 或者使用自定义注解`@Log`,通过`@annotation`表达式匹配:
```
javaCopy Code
@Around("@annotation(com.itheima.anno.Log)")
```