当前位置: 首页 > news >正文

实用指南:12. AOP(记录日志)

AOP 学习笔记

一、AOP 基础认知

1. 什么是 AOP

  • 定义:AOP(Aspect Oriented Programming)即面向切面编程,核心是面向特定方法编程,将重复逻辑与业务逻辑分离,实现功能增强。
  • 本质:不修改原始业务代码,通过动态代理技术对目标方法进行增强,解决代码重复、侵入性强的问题。

2. AOP 核心优势

3. 核心概念

概念定义
连接点(JoinPoint)可被 AOP 控制的方法(如业务层所有方法),封装了方法执行时的相关信息
通知(Advice)抽取的重复逻辑(共性功能),体现为具体方法(如计时、日志记录)
切入点(PointCut)匹配连接点的条件(通过表达式描述),决定通知应用于哪些方法
切面(Aspect)通知与切入点的对应关系(通知+切入点),所在类为切面类(@Aspect 标识)
目标对象(Target)通知所应用的原始业务对象
代理对象Spring AOP 底层通过动态代理生成,用于增强目标对象的方法

4. 底层原理

Spring AOP 基于动态代理技术实现:程序运行时自动为目标对象生成代理对象,在代理对象中嵌入通知逻辑,实现对原始方法的增强。

二、AOP 入门实战

1. 需求

统计部门管理业务层方法的执行耗时。

2. 实现步骤

(1)导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)编写切面类
@Component
@Aspect // 标识为切面类
@Slf4j
public class RecordTimeAspect {
// 环绕通知 + 切入点表达式(匹配目标方法)
@Around("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
// 1. 记录开始时间
long begin = System.currentTimeMillis();
// 2. 执行原始业务方法
Object result = pjp.proceed();
// 3. 记录结束时间并计算耗时
long end = System.currentTimeMillis();
log.info("方法执行耗时: {}毫秒", end - begin);
// 4. 返回原始方法结果
return result;
}
}
(3)测试效果

启动服务后,调用业务接口,控制台会输出对应方法的执行耗时。

3. 常见应用场景

三、AOP 进阶知识

1. 通知类型

Spring AOP 提供 5 种通知类型,覆盖方法执行的不同阶段:

通知注解执行时机注意事项
@Around(环绕通知)目标方法执行前 + 执行后需调用 proceed() 执行原始方法,必须返回结果
@Before(前置通知)目标方法执行前无返回值,不能阻止目标方法执行
@After(后置通知)目标方法执行后(无论是否抛出异常)无返回值
@AfterReturning目标方法正常执行完成后(无异常)可获取方法返回值
@AfterThrowing目标方法抛出异常后可获取异常信息
通知执行顺序(无异常情况)

@Around(前)@Before → 目标方法 → @Around(后)@AfterReturning@After

异常情况

@Around(前)@Before → 目标方法(抛异常) → @AfterThrowing@After@Around 后续逻辑不执行)

2. 切入点表达式

用于描述需要匹配的目标方法,核心有 2 种形式:

(1)execution 表达式(常用)
(2)@annotation 表达式(灵活匹配)

通过自定义注解标记目标方法,适合无规则的方法匹配:

  1. 定义自定义注解:
    @Target(ElementType.METHOD) // 仅作用于方法
    @Retention(RetentionPolicy.RUNTIME) // 运行时生效
    public @interface LogOperation {}
  2. 在目标方法上添加注解:
    @Service
    public class DeptServiceImpl implements DeptService {
    @Override
    @LogOperation // 标记需要增强的方法
    public void delete(Integer id) {
    deptMapper.delete(id);
    }
    }
  3. 切面类中引用注解:
    @Before("@annotation(com.itheima.anno.LogOperation)")
    public void before(JoinPoint joinPoint) {
    log.info("前置通知:记录操作日志");
    }
切入点表达式复用

使用 @Pointcut 抽取公共表达式,避免重复编写:

@Aspect
@Component
public class MyAspect {
// 抽取公共切入点表达式
@Pointcut("execution(* com.itheima.service.*.*(..))")
private void pt() {}
// 引用公共表达式
@Before("pt()")
public void before() {
log.info("前置通知...");
}
}

3. 通知顺序控制

当多个切面类匹配同一个目标方法时,通过以下方式控制执行顺序:

  1. 默认规则:按切面类名的字母顺序排序(前置通知:字母靠前先执行;后置通知:字母靠前后执行)
  2. @Order 注解(推荐):在切面类上添加 @Order(数字),数字越小,优先级越高
    @Aspect
    @Component
    @Order(1) // 优先级高于 Order(2) 的切面
    public class MyAspect1 { ... }

四、AOP 案例:操作日志记录

1. 需求

记录系统中增删改接口的操作日志,包含:操作人、操作时间、类名、方法名、参数、返回值、执行时长,存入数据库。

2. 实现步骤

(1)准备工作
  1. 数据库表设计:
    create table operate_log(
    id int unsigned primary key auto_increment comment 'ID',
    operate_emp_id int unsigned comment '操作人ID',
    operate_time datetime comment '操作时间',
    class_name varchar(100) comment '操作的类名',
    method_name varchar(100) comment '操作的方法名',
    method_params varchar(1000) comment '方法参数',
    return_value varchar(2000) comment '返回值',
    cost_time int comment '方法执行耗时(ms)'
    ) comment '操作日志表';
  2. 实体类 OperateLog + Mapper 接口(提供 insert 方法)
(2)编码实现
  1. 自定义注解 @LogOperation(标记需要记录日志的接口方法)
  2. 切面类实现:
    @Aspect
    @Component
    public class OperationLogAspect {
    @Autowired
    private OperateLogMapper operateLogMapper;
    @Around("@annotation(com.itheima.anno.LogOperation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    // 1. 记录开始时间
    long startTime = System.currentTimeMillis();
    // 2. 执行原始方法
    Object result = joinPoint.proceed();
    // 3. 计算耗时
    long costTime = System.currentTimeMillis() - startTime;
    // 4. 构建日志对象
    OperateLog log = new OperateLog();
    log.setOperateEmpId(CurrentHolder.getCurrentId()); // 从 ThreadLocal 获取当前登录人ID
    log.setOperateTime(LocalDateTime.now());
    log.setClassName(joinPoint.getTarget().getClass().getName()); // 目标类名
    log.setMethodName(joinPoint.getSignature().getName()); // 目标方法名
    log.setMethodParams(Arrays.toString(joinPoint.getArgs())); // 方法参数
    log.setReturnValue(result.toString()); // 返回值
    log.setCostTime(costTime);
    // 5. 保存日志到数据库
    operateLogMapper.insert(log);
    return result;
    }
    }
  3. 在 Controller 层增删改方法上添加 @LogOperation 注解

3. 关键技术:ThreadLocal 共享登录信息

  • 作用:在同一线程(同一请求)中共享数据(如当前登录人ID),实现线程隔离
  • 工具类实现
    public class CurrentHolder {
    private static final ThreadLocal<Integer> CURRENT_LOCAL = new ThreadLocal<>();// 设置当前登录人IDpublic static void setCurrentId(Integer empId) {CURRENT_LOCAL.set(empId);}// 获取当前登录人IDpublic static Integer getCurrentId() {return CURRENT_LOCAL.get();}// 移除数据(避免内存泄漏)public static void remove() {CURRENT_LOCAL.remove();}}
  • 使用场景:在 Token 过滤器中解析登录人ID并存入 ThreadLocal,在 AOP 中直接获取。

四、核心总结

  1. AOP 核心思想是分离共性逻辑与业务逻辑,通过切面类统一管理增强功能
  2. 5 种通知类型覆盖方法执行全生命周期,@Around 功能最强大(可控制原始方法执行)
  3. 切入点表达式两种形式:execution(按方法签名匹配)、@annotation(按注解匹配)
  4. 多切面执行顺序通过 @Order 控制,数字越小优先级越高
  5. 实际开发中常用 AOP 实现日志、权限、事务等非业务功能,提高代码复用性和可维护性
http://www.jsqmd.com/news/280006/

相关文章:

  • springboot基于Java的外卖管理系统设计开发实现
  • 2026主管护师护理学怎么备考,全流程指南稳步通关不跑偏
  • ai做PPT正确打开方式:选对工具+用对方法,3分钟搞定专业演示
  • 谷歌发布AI广告与分析顾问:是SEO革命性产品,还是未来可期?
  • 深圳昊客/百度竞价开户推广代运营服务商:推荐排名前5的公司
  • git使用--depth参数参数快速拉取分支代码后无法切换到其他分支解决办法
  • springboot基于java的网吧管理系统设计实现
  • 金华宠物医院优选推荐,2026年宠主好评TOP榜单,猫咪体检/宠物神经外科/宠物眼科/母猫绝育,宠物医生排名前十
  • Mysql优化笔记 - 倾听
  • springboot基于Java的诊所管理系统设计实现
  • 为应对“差评勒索”,谷歌上月推出的负面评论敲诈举报表单似乎非常有效
  • 2024年提示工程热点:生命周期管理的4个关键进化方向
  • AcWing 3714:砍树 ← 线性 DP(北京师范大学考研机试题)
  • 挑战Sora!以色列独角兽Lightricks发布LTX-2
  • 一加7刷入twrp
  • springboot基于java零售与仓储管理系统的设计与实现
  • 2025年AI超级员工公司综合排名权威发布,AI企业员工/AI智能员工/AI超级员工/AI员工品牌口碑排行
  • 如何解析iOS崩溃日志:从获取到符号化分析 - 指南
  • 深圳昊客网络|百度推广开户竞价代运营公司/服务商:推荐排名前十的机构
  • 告别噪音与回音!WX-0813 AI 语音处理模组,重塑音频通话体验
  • 告别玄学Prompt!Agent Skills让AI Agent真正干活,收藏级教程
  • 如何低成本、快速地建立私有内测系统?
  • 2026卫生职称考试备考资源准确选择攻略
  • 2026年防腐环保板材排行榜,板材品牌哪家强?权威榜单推荐
  • 【github】学生认证Azure免费云服务器
  • 完整教程:DBA 运维 数据库 备份 还原 MSSQL
  • springboot基于JavaWeb的“校园集市”管理系统
  • NTS-886003-ntp服务器
  • 救命神器!8个AI论文网站测评:研究生开题报告必备清单
  • 深圳百度推广代运营排名前十机构怎么选?昊客网络用技术实力说话!