news 2026/4/18 8:03:03

SpringAOP:面向切面编程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringAOP:面向切面编程

大家好,这依旧是一篇个人笔记,关于SpringAOP的笔记

笔记内容来源于黑马Web课程133~139集

本篇笔记由WorkBuddy一起辅助完成,不喜勿喷,谢谢!

一、Spring AOP 整体思路

Spring AOP(Aspect Oriented Programming,面向切面编程)是一种编程思想,核心思想是:在不修改原有业务代码的情况下,为指定的方法统一添加额外的通用功能。

1.1 AOP 核心思想

AOP 的本质是「面向方法编程」,通过「切面」将「额外功能」与「作用范围」结合起来:

  • • 切面(Aspect):额外功能 + 作用范围的组合
  • • 额外功能:如统计耗时、日志记录、权限校验等通用逻辑
  • • 作用范围:通过切入点表达式指定哪些方法需要添加额外功能

总结:AOP编程思想“面向切面”或者“面向方法”编程本质上就是给“你规定的一系列方法”加上“额外功能”

所谓“切面”就是指“额外功能”+“作用范围”(就是我规定的哪些方法)

以上面图片为例:本质上就是给“业务层的方法”添加“计算耗时”这个额外功能

1.2 AOP 执行流程

AOP 的执行基于「动态代理」机制:

步骤

说明

1. 定义切面类

使用 @Aspect 注解标识切面类,@Component 纳入 Spring 管理

2. 定义切入点

使用 @Pointcut 或直接在通知注解中指定目标方法范围

3. 定义通知

使用 @Around/@Before 等注解定义额外功能的执行时机

4. 生成代理对象

Spring 为目标对象创建代理对象(JDK 动态代理或 CGLIB)

5. 调用代理方法

Controller 实际调用的是代理对象的方法

6. 执行通知逻辑

代理对象先执行通知逻辑,再调用目标方法

二、Spring AOP 基础

2.1 AOP 快速入门

2.1.1 场景需求

统计所有业务层方法的执行耗时,用于性能分析和优化。

2.1.2 实现步骤

步骤一:引入 AOP 依赖(pom.xml)

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

步骤二:编写切面类

@Aspect // 标明这是一个 AOP 类 @Component // 纳入 Spring 容器管理 public class RecordTimeAspect { @Around("execution(* com.itheima.service.impl.*.*(..))") public Object recordTime(ProceedingJoinPoint pjp) throws Throwable { // 1. 记录开始时间 long begin = System.currentTimeMillis(); // 2. 执行目标方法 Object result = pjp.proceed(); // 3. 记录结束时间并计算耗时 long end = System.currentTimeMillis(); log.info("方法执行耗时: {} ms", end - begin); return result;

2.2 AOP 核心概念

概念

说明

连接点 (JoinPoint)

可以被 AOP 控制的方法,包含方法执行时的相关信息

通知 (Advice)

需要添加的额外功能(共性逻辑),最终体现为一个方法

切入点 (PointCut)

匹配连接点的条件,通知仅在切入点方法执行时被应用

切面 (Aspect)

描述通知与切入点的对应关系(通知 + 切入点)

目标对象 (Target)

通知所应用的对象(被代理的原始对象)

2.3 AOP 的优势

  • • 减少重复代码:通用逻辑统一维护
  • • 代码无侵入:不修改原有业务代码
  • • 提高开发效率:复用通用功能
  • • 维护方便:修改一处,全局生效

三、Spring AOP 进阶

3.1 通知类型

根据通知方法执行时机的不同,分为以下五类:

通知类型

执行时机

特点

@Around

目标方法前、后都执行

环绕通知,需手动调用 proceed(),最常用

@Before

目标方法前执行

前置通知

@After

目标方法后执行

后置通知,无论是否异常都执行

@AfterReturning

目标方法正常返回后执行

返回后通知,有异常不执行

@AfterThrowing

目标方法抛出异常后执行

异常后通知

⚠️ 注意事项:

  • • @Around 环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来执行目标方法
  • • @Around 环绕通知方法的返回值必须指定为 Object,用于接收目标方法的返回值

3.2 通知顺序

3.2.1 默认顺序

当多个切面的切入点都匹配到目标方法时,默认按照切面类的「类名字母顺序」排序:

  • • 目标方法前的通知:字母排名靠前的先执行
  • • 目标方法后的通知:字母排名靠前的后执行

3.2.2 自定义顺序(@Order)

使用 @Order(数字) 注解控制执行顺序,数字越小越先执行:

@Order(5) // 先执行 @Aspect @Component public class MyAspect3 {@Before("execution(* com.itheima.service.impl.*.*(..))")public void before(){ log.info("MyAspect3 -> before ..."); } @Order(8) // 后执行 @Aspect @Component public class MyAspect2 {@Before("execution(* com.itheima.service.impl.*.*(..))")public void before(){ log.info("MyAspect3 -> before ..."); }

3.3 切入点表达式

3.3.1 execution 表达式

execution 是最常用的切入点表达式,语法如下:

execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

通配符说明:

通配符

含义

*

单个独立的任意符号,可匹配任意返回值、包名、类名、方法名、参数

..

多个连续的任意符号,可匹配任意层级的包或任意个数、类型的参数

常用示例:

// 匹配 service 包下所有类的所有方法 execution(* com.itheima.service.*.*(..)) // 匹配所有以 del 开头的方法 execution(* com.itheima.service.*.del*(..)) // 匹配所有以 e 结尾的方法 execution(* com.itheima.service.*.*e(..)) // 同时匹配多个方法(使用 || 运算符) execution(* com.itheima.service.*.list(..)) || execution(* com.itheima.service.*.delete(..))

书写建议:

  • • 所有业务方法名命名时尽量规范,方便切入点表达式快速匹配
  • • 描述切入点方法通常基于接口描述,增强拓展性
  • • 在满足业务需要的前提下,尽量缩小切入点的匹配范围

3.3.2 @annotation 表达式

基于自定义注解标记方法,而非直接写方法路径,更加灵活:

补充一下关于annotation切入点表达式的理解:本质上就是定义了一个自己想要的注解,然后再切面类的通知方法里面绑定归属于哪一个自己定义的注解

步骤一:定义自定义注解

步骤二:切面类绑定注解

步骤三:在目标方法上使用注解

3.3.3 @PointCut 抽取公共切入点

将公共的切入点表达式抽取出来,提高复用性:

PointCut:对于公共切入点(方法路径)的简化;作用就像是springboot里面RequestMapping公共路径书写的作用

@Pointcut("execution(* com.itheima.service.impl.*.*(..))") private void pt() {} // private:仅当前切面类可用 @Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { // 通知逻辑 }

注意:

• private:仅能在当前切面类中引用该表达式

• public:在其他外部的切面类中也可以引用该表达式

3.4 连接点 JoinPoint

就是你要在Aspect类里面执行原方法的内容就必须用到这个JointPoint

在 Spring 中用 JoinPoint 抽象了连接点,可以获得方法执行时的相关信息:

API 方法

说明

joinPoint.getTarget()

获取目标对象

joinPoint.getTarget().getClass().getName()

获取目标类全名

joinPoint.getSignature().getName()

获取目标方法名

joinPoint.getArgs()

获取目标方法参数数组

joinPoint.proceed()

执行目标方法(仅 ProceedingJoinPoint 可用)

注意:

  • 对于 @Around 通知,获取连接点信息只能使用 ProceedingJoinPoint
  • • 对于其他四种通知,获取连接点信息使用 JoinPoint(ProceedingJoinPoint 的父类)

四、Spring AOP 案例

4.1 案例:记录操作日志

将增、删、改相关接口的操作日志记录到数据库表中。

4.1.1 日志信息内容

  • • 操作人:当前登录员工的 ID
  • • 操作时间:方法执行的时间
  • • 执行方法的全类名、方法名
  • • 方法运行时参数、返回值
  • • 方法执行时长

4.1.2 技术方案

项目

选择

通知类型

@Around 环绕通知

切入点表达式

@annotation(com.example.tlias_management.anno.Log)

4.2 案例:获取当前登录员工

通过 ThreadLocal 在拦截器和切面之间传递当前登录员工信息。

4.2.1 实现思路

输出操作人实现思路:操作的时候需要登陆认证,会产生JWT令牌,之前我们定义的JWT令牌自定义了信息“id”,而这个id可以唯一标识每一个员工,因此获取到这个id就相当于知道操作人是谁了

  • • 登录时生成 JWT 令牌,包含员工 ID
  • • 拦截器解析 Token,将员工 ID 存入 ThreadLocal
  • • 切面类从 ThreadLocal 获取操作人 ID
  • • 请求结束后清除 ThreadLocal(防止内存泄漏)

4.2.2 ThreadLocal 工具类

先准备这个线程类,每次请求的数据都会保存在独立的一次线程中,保证了获取到的数据就是那次请求的数据

4.2.3 拦截器设置 ThreadLocal

@Component public class TokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("token"); Claims claims = JwtUtils.parseToken(token); Integer employeeId = Integer.valueOf(claims.get("id").toString()); CurrentHolder.setCurrentId(employeeId); // 存入 ThreadLocal return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { CurrentHolder.remove(); // 清除 ThreadLocal } }

4.2.4 切面类获取操作人

@Around("@annotation(com.example.tlias_management.anno.Log)") public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable { // 从 ThreadLocal 获取操作人 ID Integer operateEmpId = CurrentHolder.getCurrentId(); // 其他日志信息... LocalDateTime operateTime = LocalDateTime.now(); String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); // 执行目标方法 Object result = joinPoint.proceed(); // 保存日志到数据库... return result;

五、知识总结

5.1 核心概念回顾

概念

一句话总结

AOP

面向切面编程,不修改原代码给方法加额外功能

切面 (Aspect)

通知 + 切入点的组合

通知 (Advice)

要添加的额外功能

切入点 (PointCut)

指定哪些方法需要添加通知

连接点 (JoinPoint)

可以被 AOP 控制的方法

目标对象 (Target)

被代理的原始对象

5.2 常用注解

注解

作用

@Aspect

标识这是一个切面类

@Component

将切面类纳入 Spring 容器管理

@Around/@Before/@After

定义通知类型和执行时机

@Pointcut

抽取公共切入点表达式

@Order

控制多个切面的执行顺序

5.3 切入点表达式

类型

使用场景

execution

按方法路径匹配,适合批量匹配

@annotation

按自定义注解匹配,更加灵活精准

5.4 注意事项

  • • @Around 必须调用 proceed() 执行目标方法
  • • @Around 返回值必须是 Object
  • • 使用 ThreadLocal 后一定要在 afterCompletion 中 remove()
  • • 切入点表达式尽量缩小匹配范围,提高性能
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:56:49

显卡性能深度调校:NVIDIA Profile Inspector隐藏参数实战指南

显卡性能深度调校&#xff1a;NVIDIA Profile Inspector隐藏参数实战指南 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector NVIDIA Profile Inspector是一款专为高级用户设计的显卡驱动配置工具&#xff…

作者头像 李华
网站建设 2026/4/18 7:56:45

美国AI安全公司让AI当CEO开实体店,运营乌龙不断引思考

美国AI安全初创公司让AI当CEO开实体店&#xff0c;运营状况频出引思考近日&#xff0c;美国AI安全初创公司Andon Labs搞了场大胆实验&#xff0c;直接任命AI担任CEO开实体店&#xff0c;豪掷10万美元&#xff08;约合人民币68.5万元&#xff09;启动资金&#xff0c;让它独立运…

作者头像 李华
网站建设 2026/4/18 7:56:34

Adobe-GenP 3.0:Adobe Creative Cloud通用补丁技术解析与使用指南

Adobe-GenP 3.0&#xff1a;Adobe Creative Cloud通用补丁技术解析与使用指南 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP Adobe-GenP 3.0是一款基于AutoIt脚本开…

作者头像 李华
网站建设 2026/4/18 7:54:13

Scroll Reverser深度解析:重新定义你的macOS滚动体验

Scroll Reverser深度解析&#xff1a;重新定义你的macOS滚动体验 【免费下载链接】Scroll-Reverser Per-device scrolling prefs on macOS. 项目地址: https://gitcode.com/gh_mirrors/sc/Scroll-Reverser 还在为macOS触控板和鼠标的滚动方向冲突而烦恼吗&#xff1f;Sc…

作者头像 李华
网站建设 2026/4/18 7:53:19

MogFace人脸检测模型-large:电商场景下的人脸识别应用全解析

MogFace人脸检测模型-large&#xff1a;电商场景下的人脸识别应用全解析 1. 电商场景中的人脸检测挑战与机遇 在电商行业蓬勃发展的今天&#xff0c;商品图片处理已成为日常运营中不可或缺的环节。特别是对于服装、美妆、配饰等品类&#xff0c;商品主图中的人脸检测与处理直…

作者头像 李华