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

相关推荐
C雨后彩虹1 小时前
CAS与其他并发方案的对比及面试常见问题
java·面试·cas·同步·异步·
sww_10262 小时前
RAG检索增强 ETL最佳实战
人工智能·python·spring
java1234_小锋2 小时前
Java高频面试题:SpringBoot为什么要禁止循环依赖?
java·开发语言·面试
2501_944525543 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 账户详情页面
android·java·开发语言·前端·javascript·flutter
计算机学姐3 小时前
基于SpringBoot的电影点评交流平台【协同过滤推荐算法+数据可视化统计】
java·vue.js·spring boot·spring·信息可视化·echarts·推荐算法
Filotimo_3 小时前
Tomcat的概念
java·tomcat
索荣荣3 小时前
Java Session 全面指南:原理、应用与实践(含 Spring Boot 实战)
java·spring boot·后端
Amumu121383 小时前
Vue Router(二)
java·前端
念越4 小时前
数据结构:栈堆
java·开发语言·数据结构