使用Spring AOP实现@Log注解记录请求参数和执行时间
下面我将展示如何使用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
高级定制
可以根据需要扩展这个切面:
- 记录返回值:在切面中添加返回值的逻辑
- 异常处理:添加
@AfterThrowing
建议来记录异常情况 - 自定义日志格式:修改日志输出格式
- 敏感信息过滤:对参数中的敏感信息进行过滤
- 异步方法支持:添加对异步方法的支持
这个实现提供了灵活的控制,通过注解参数可以决定是否打印参数和执行时间,还可以自定义方法名,非常适合生产环境使用。