Spring AOP 实现日志切面记录功能详解

一、前言

在实际项目开发中,日志记录是非常重要的一个环节,它可以帮助我们追踪请求流程、排查问题、监控系统运行状态等。本文将详细介绍如何使用Spring AOP技术实现方法级别的日志记录功能。

二、核心组件介绍

1. 自定义注解 LogAnnotation

java 复制代码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
}
  • @Target : 指定注解可以应用的位置,包括方法(METHOD)和类型(TYPE)
  • @Retention : 设置注解保留策略为运行时(RUNTIME),确保在运行时可以通过反射获取注解信息
  • 这个注解作为一个标记,用于标识需要进行日志记录的方法或类

2. AOP 切面类 LogAspectAop

java 复制代码
@Aspect
@Component
public class LogAspectAop {
    private final static Logger logger = LoggerFactory.getLogger(LogAspectAop.class);
  • @Aspect: 标记这是一个切面类
  • @Component: 将切面类注册为Spring容器中的Bean
  • 使用SLF4J作为日志框架

三、核心实现分析

1. 切点定义

java 复制代码
@Pointcut("@annotation(jerry.annotation.LogAnnotation)")
public void logAspectAop() {
}
  • 定义切点表达式,匹配所有使用@LogAnnotation注解的方法
  • 当目标方法被@LogAnnotation注解标记时,AOP会拦截并执行相应逻辑

2. 方法环绕处理

java 复制代码
 @Around("logAspectAop()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String reqId = UUID.randomUUID().toString();
        try {

            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();

            long startTime = System.currentTimeMillis();

            logger.info("REQUEST_ID: {}, Request Info - URL: {}, HTTP_METHOD: {}, IP: {}, CLASS_METHOD: {}.{}, ARGS: {}, TIME: {}",
                    reqId,
                    request.getRequestURL().toString(),
                    request.getMethod(),
                    request.getRemoteAddr(),
                    joinPoint.getSignature().getDeclaringTypeName(),
                    joinPoint.getSignature().getName(),
                    JSON.toJSONString(joinPoint.getArgs()),
                    MyDateUtil.formatDateTime(LocalDateTime.now()));

            // 执行目标方法
            Object result = joinPoint.proceed();

            long executionTime = System.currentTimeMillis() - startTime;

            logger.info("REQUEST_ID: {}, RESPONSE : {}, EXECUTION_TIME: {}ms, TIME: {}",
                    reqId,
                    new ObjectMapper().writeValueAsString(result),
                    executionTime,
                    MyDateUtil.formatDateTime(LocalDateTime.now()));

            return result;
        } catch (Exception e) {
            logger.error("REQUEST_ID: {}, ERROR: {}, TIME: {}", reqId, e.getMessage(), MyDateUtil.formatDateTime(LocalDateTime.now()), e);
            throw e;
        }
    }

方法实现了以下功能:

  • 统一请求追踪 :使用 UUID 生成唯一的 reqId 来关联请求和响应日志
  • 性能监控:记录接口执行时间,计算执行的时间
  • 完整日志记录:记录请求信息(URL、方法、IP、参数等)和响应结果
  • 异常处理:捕获并记录异常信息,同时将异常重新抛出
  • 环绕通知 :使用 @Around 注解实现方法执行前后的完整拦截

该方法解决了原来 @Before 和 @AfterReturning 分离导致的关联性问题,通过 reqId 实现了请求响应的精准匹配。

四、使用方法

在需要记录日志的方法上添加@LogAnnotation注解:

java 复制代码
@GetMapping("/test")
@ApiOperation("测试日志打印")
@LogAnnotation
public String someMethod(String param) {
    // 业务逻辑
    return "result";
}

当调用该方法时,AOP会自动记录请求信息和响应结果。

五、设计优势

  1. 无侵入性: 通过注解方式实现,不影响原有业务代码
  2. 灵活配置: 可以选择性地对特定方法添加日志记录
  3. 统一管理: 集中处理日志记录逻辑,便于维护
  4. 信息全面: 记录了请求和响应的完整信息,便于问题排查

六、注意事项

  1. 性能考虑: 日志记录会带来一定的性能开销,对于高频调用的方法需谨慎使用
  2. 敏感信息: 注意过滤敏感参数信息,避免泄露用户隐私
  3. 异常处理 : 代码中使用了catch(ignore)模式,确保日志异常不影响业务流程
  4. 依赖引入: 需要引入AOP、JSON处理等相关依赖包

七、总结

通过Spring AOP和自定义注解的方式,我们可以轻松实现方法级别的日志记录功能。这种方式既保持了代码的简洁性,又提供了强大的日志记录能力,是企业级应用开发中的常用实践方案。

相关推荐
苏supper7 小时前
Spring Boot多环境配置与打包运行
spring boot·后端·spring
geovindu7 小时前
rust: Composite Pattern
开发语言·后端·设计模式·rust·组合模式
SearchMan8 小时前
还有人不知道&和&&的区别?
后端
想用offer打牌8 小时前
Personal Agent爆火 - 它到底是个什么
人工智能·后端·ai编程
IT_陈寒8 小时前
Vue的v-if和v-for混用居然是个天坑
前端·人工智能·后端
用户1305180712158 小时前
JDBC学习DAY2:从Statement到PreparedStatement
java
蛋蛋的就会顺顺的8 小时前
hot100——矩阵
java·数据结构·算法·leetcode·力扣
周杰偷奶茶9 小时前
【Java】数组的定义和使用(附管理系统实战案例)
java·开发语言
橙子圆1239 小时前
JUC之集合类不安全
java·开发语言
打工仔折腾 AI10 小时前
从Attention到BERT:双向预训练语言模型到底解决了什么问题
人工智能·后端·python·深度学习·语言模型·bert