使用Spring AOP实现@Log注解记录请求参数和执行时间

发布于:2025-07-05 ⋅ 阅读:(18) ⋅ 点赞:(0)

下面我将展示如何使用Spring AOP来实现一个@Log注解,用于记录Controller方法的请求参数和执行时间。

1. 创建自定义注解

首先创建一个@Log注解:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    String value() default "";
    boolean printArgs() default true;  // 是否打印参数
    boolean printTime() default true;  // 是否打印执行时间
}

2. 创建AOP切面类

创建一个切面类来处理@Log注解:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    // 定义切点:所有带有@Log注解的方法
    @Pointcut("@annotation(com.yourpackage.annotation.Log)")
    public void logPointCut() {}

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 获取注解
        Log logAnnotation = method.getAnnotation(Log.class);
        String methodName = logAnnotation.value().isEmpty() 
            ? method.getDeclaringClass().getSimpleName() + "." + method.getName() 
            : logAnnotation.value();
        
        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
        
        // 记录请求信息
        if (request != null && logAnnotation.printArgs()) {
            logger.info("请求开始 => 方法名: {}, URL: {}, HTTP方法: {}, IP: {}, 参数: {}",
                    methodName,
                    request.getRequestURL().toString(),
                    request.getMethod(),
                    request.getRemoteAddr(),
                    Arrays.toString(joinPoint.getArgs()));
        } else if (logAnnotation.printArgs()) {
            logger.info("请求开始 => 方法名: {}, 参数: {}",
                    methodName,
                    Arrays.toString(joinPoint.getArgs()));
        } else {
            logger.info("请求开始 => 方法名: {}", methodName);
        }
        
        // 记录执行时间
        long startTime = System.currentTimeMillis();
        Object result;
        try {
            result = joinPoint.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            if (logAnnotation.printTime()) {
                logger.info("请求结束 => 方法名: {}, 执行耗时: {}ms",
                        methodName,
                        endTime - startTime);
            }
        }
        
        return result;
    }
}

3. 在Controller中使用注解

在Controller方法上使用@Log注解:

import com.yourpackage.annotation.Log;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class TestController {

    @Log("测试方法1")
    @GetMapping("/test1")
    public String test1(@RequestParam String name) {
        return "Hello " + name;
    }

    @Log(printArgs = false) // 不打印参数
    @PostMapping("/test2")
    public User test2(@RequestBody User user) {
        return user;
    }

    @Log(printTime = false) // 不打印执行时间
    @GetMapping("/test3")
    public String test3() {
        return "Simple test";
    }
}

4. 确保Spring Boot配置正确

确保你的Spring Boot应用已经启用了AOP支持。在Spring Boot中,AOP默认是启用的,但如果你需要确认,可以在主类上添加@EnableAspectJAutoProxy

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5. 日志输出示例

当请求到达被注解的方法时,日志输出类似这样:

请求开始 => 方法名: 测试方法1, URL: http://localhost:8080/api/test1, HTTP方法: GET, IP: 127.0.0.1, 参数: [John]
请求结束 => 方法名: 测试方法1, 执行耗时: 12ms

高级定制

可以根据需要扩展这个切面:

  1. 记录返回值​:在切面中添加返回值的逻辑
  2. 异常处理​:添加@AfterThrowing建议来记录异常情况
  3. 自定义日志格式​:修改日志输出格式
  4. 敏感信息过滤​:对参数中的敏感信息进行过滤
  5. 异步方法支持​:添加对异步方法的支持

这个实现提供了灵活的控制,通过注解参数可以决定是否打印参数和执行时间,还可以自定义方法名,非常适合生产环境使用。


网站公告

今日签到

点亮在社区的每一天
去签到