当前位置: 首页 > news >正文

使用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

高级定制

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

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

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

http://www.lryc.cn/news/580182.html

相关文章:

  • Linux基础 -- NAND Flash UBIFS基础特性及注意点
  • Adobe Illustrator设置的颜色和显示的颜色不对应问题
  • 新手快速入门Luban+Unity使用
  • OneCode 智能化UI布局与定位:注解驱动的视觉编排艺术
  • 打通线上线下会议室联动的综合解决方案及技术选型
  • Echarts3D柱状图-圆柱体-文字在柱体上垂直显示的实现方法
  • D3 面试题100道之(21-40)
  • 如何查看自己电脑的CUDA版本?
  • 服务器间接口安全问题的全面分析
  • 学习者的Python项目灵感
  • 本地区块链服务在物联网中的应用实例
  • Rust+Blender:打造高性能游戏引擎
  • OneCode图生代码技术深度解析:从可视化设计到注解驱动实现的全链路架构
  • golang 中当 JSON 数据缺少结构体(struct)中定义的某些字段,会有异常吗
  • 【HDMI CEC】 设备 OSD 名称功能详解
  • Rust match 控制流结构
  • 从0开始学习R语言--Day38--辛普森多样性指数
  • 重学前端002 --响应式网页设计 CSS
  • 【网络安全基础】第三章---公钥密码和消息认证
  • <tauri><rust><GUI>使用tauri创建一个文件夹扫描程序
  • 【网络】Linux 内核优化实战 - net.core.flow_limit_table_len
  • C++26 下一代C++标准
  • 深度学习笔记29-RNN实现阿尔茨海默病诊断(Pytorch)
  • 倾斜摄影无人机飞行航线规划流程详解
  • 前端开发-前置知识
  • 2025.7.4总结
  • 物联网数据安全区块链服务
  • DeepSeek-R1知识蒸馏和微调实践(一)源码
  • 使用 C# 发送电子邮件(支持普通文本、HTML 和附件)
  • BEVFormer模型处理流程