Spring Boot实现AOP日志案例

wen java案例 1

本文目录导读:

Spring Boot实现AOP日志案例

  1. 目录导读
  2. 为什么需要AOP日志?—— 摆脱硬编码的痛点
  3. 核心概念速览:AOP术语与Spring Boot自动配置
  4. 实战案例:自定义注解 + 切面实现全链路日志
  5. 进阶技巧:动态获取用户、请求参数与耗时统计
  6. 性能与安全考量:异步记录与敏感数据脱敏
  7. 常见问题与问答(FAQ)

Spring Boot实现AOP日志案例:从零构建可观测性的最佳实践

目录导读

  1. 为什么需要AOP日志?—— 摆脱硬编码的痛点
  2. 核心概念速览:AOP术语与Spring Boot自动配置
  3. 实战案例:自定义注解 + 切面实现全链路日志
  4. 进阶技巧:动态获取用户、请求参数与耗时统计
  5. 性能与安全考量:异步记录与敏感数据脱敏
  6. 常见问题与问答(FAQ)

为什么需要AOP日志?—— 摆脱硬编码的痛点

在日常开发中,若在每个业务方法内手动编写logger.info("..."),不仅代码冗余、易遗漏,且后期维护成本极高,而面向切面编程(AOP) 允许我们将日志记录、权限校验等横切逻辑从业务代码中剥离,通过声明式方式动态织入,Spring Boot对AOP提供了开箱即用的支持,仅需引入spring-boot-starter-aop即可。

核心概念速览:AOP术语与Spring Boot自动配置

  • 切面(Aspect):横切关注点的模块化,如日志切面。
  • 连接点(JoinPoint):程序执行点(如方法调用)。
  • 通知(Advice):切面在特定连接点执行的动作(@Before@AfterReturning@Around等)。
  • 切入点(Pointcut):匹配连接点的表达式(如execution(* com.example.service.*.*(..)))。

Spring Boot会自动注册AnnotationAwareAspectJAutoProxyCreator,无需额外XML配置,我们只需在切面类上标注@Aspect@Component即可生效。

实战案例:自定义注解 + 切面实现全链路日志

创建自定义注解@LogRecord

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRecord {
    String value() default ""; // 业务描述,如"查询用户信息"
}

编写日志切面

@Aspect
@Component
@Slf4j
public class LogAspect {
    // 匹配所有标注了@LogRecord的方法
    @Around("@annotation(logRecord)")
    public Object handleLog(ProceedingJoinPoint joinPoint, LogRecord logRecord) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();
        log.info("【开始】{} | 参数: {}", logRecord.value(), Arrays.toString(args));
        Object result;
        try {
            result = joinPoint.proceed();
            long cost = System.currentTimeMillis() - startTime;
            log.info("【结束】{} | 耗时: {}ms | 返回: {}", logRecord.value(), cost, JSON.toJSONString(result));
            return result;
        } catch (Throwable e) {
            log.error("【异常】{} | 错误: {}", logRecord.value(), e.getMessage(), e);
            throw e;
        }
    }
}

进阶技巧:动态获取用户、请求参数与耗时统计

通过RequestContextHolder获取当前HTTP请求上下文,从而记录操作人IP、Session等信息:

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
    HttpServletRequest request = attributes.getRequest();
    String ip = request.getRemoteAddr();
    String user = request.getHeader("X-User-Id"); // 或从SecurityContext获取
    // 将user、ip追加到日志中
}

利用@Aroundproceed()前后时间戳,精确计算方法执行耗时,建议将耗时超过阈值(如1000ms)的日志单独标记为WARN级别,便于性能监控。

性能与安全考量:异步记录与敏感数据脱敏

  • 异步化:在切面中直接写日志可能增加请求响应时间,推荐使用@Async注解或AsyncLogService,将日志写入MQ或独立线程池处理。
  • 数据脱敏:对手机号、身份证等敏感字段,在打印前用替换,可使用Jackson@JsonProperty(access = WRITE_ONLY)或自定义JsonSerializer
  • 避免循环依赖:切面中若注入业务Service,请使用@Lazy注解避免代理失效。

常见问题与问答(FAQ)

Q1:为什么我的@Aspect切面不生效? A:请检查:① 是否已引入spring-boot-starter-aop;② 切面类是否有@Component;③ 切入点表达式是否正确(如execution(public * com.xyz..*.*(..)));④ 确保是Spring管理的Bean调用,避免同类内部this调用。

Q2:AOP日志与logback.xml配置如何协同? A:两者互补,AOP决定“记录什么方法”,logback.xml决定“输出到哪里(控制台/文件/Redis)以及格式”,可将切面日志的logger name设为独立包名,便于单独归档。

Q3:如何实现“只记录指定包路径下的所有方法”? A:使用execution(* com.example.service..*.*(..)),其中匹配任意子包(含多层)。

Q4:AOP切面会影响事务吗? A:如果切面使用@Around且内部调用proceed(),在业务方法前添加@Transactional时,代理顺序取决于@Order,建议日志切面@Order(1),事务切面@Order(2),确保事务先行。


通过上述案例,你已掌握Spring Boot实现AOP日志的核心技巧,从自定义注解到切面编写,再到异步与脱敏优化,这一套方案不仅简化了日志代码,更提升了线上问题的定位效率,立即在你的项目中实践,体验“一次切面,处处可观测”的乐趣吧!

抱歉,评论功能暂时关闭!