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

相关推荐
想摆烂的不会研究的研究生2 小时前
并发场景——接口幂等性设计
数据库·redis·后端·缓存
灰什么鱼2 小时前
慢接口调优过程
java·空间计算·geometry
手握风云-2 小时前
JavaEE 进阶第九期:Spring MVC - Web开发的“交通枢纽”(三)
前端·spring·java-ee
静待_花开2 小时前
java日期格式化
java·开发语言
我是一只小青蛙8882 小时前
二分查找巧解数组范围问题
java·开发语言·算法
Renhao-Wan2 小时前
数据结构在Java后端开发与架构设计中的实战应用
java·开发语言·数据结构
小Ti客栈2 小时前
Spring Boot Profile 与外部化配置详解
spring·springboot
u0104058362 小时前
企业微信第三方应用API对接的Java后端架构设计:解耦与可扩展性实践
java·数据库·企业微信
sheji34162 小时前
【开题答辩全过程】以 基于Java的智慧党建管理系统的设计与实现为例,包含答辩的问题和答案
java·开发语言