news 2026/8/7 23:36:10

【后端】【Java】《Spring Boot 统一接口耗时统计实践:基于 HandlerInterceptor 的工程级方案》

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【后端】【Java】《Spring Boot 统一接口耗时统计实践:基于 HandlerInterceptor 的工程级方案》

一步一步讲清楚
👉接口耗时为什么不能写在 Controller 里?
👉在拦截器里应该怎么“正确、优雅地处理”?


一、为什么不在 Controller 里写耗时代码?

示例代码是这样的:

long start = System.currentTimeMillis(); // 业务逻辑 long cost = System.currentTimeMillis() - start; log.info("接口耗时: {} ms", cost);

❌ 问题有 4 个:

  1. 大量重复代码

    • 每个接口都要写一遍

  2. 业务代码被日志污染

  3. 容易漏写 / 写错

  4. 无法统一统计所有接口

👉这是典型的横切关注点(Cross-Cutting Concern)
👉 非常适合用:拦截器 / AOP


二、正确方案:在拦截器中统一记录接口耗时

Spring MVC 中,拦截器(HandlerInterceptor)是最合适的位置


三、拦截器记录耗时的核心思路

preHandle → 记录开始时间 controller → 业务逻辑 afterCompletion → 计算耗时 + 打日志

四、标准实现方式(推荐写法)

1️⃣ 在 preHandle 中记录开始时间

@Component public class TimeCostInterceptor implements HandlerInterceptor { private static final Logger log = LoggerFactory.getLogger(TimeCostInterceptor.class); private static final String START_TIME = "startTime"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { request.setAttribute(START_TIME, System.currentTimeMillis()); return true; }

2️⃣ 在 afterCompletion 中计算耗时

@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Long startTime = (Long) request.getAttribute(START_TIME); if (startTime == null) { return; } long cost = System.currentTimeMillis() - startTime; log.info("接口耗时 | {} {} | {} ms", request.getMethod(), request.getRequestURI(), cost); } }

3️⃣ 注册拦截器

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TimeCostInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/static/**"); } }

五、最终日志效果(真实可用)

2025-01-01 10:00:01.456 INFO [traceId=9f8a3b7c2d1a4e] 接口耗时 | GET /users/1 | 38 ms

✔ 不侵入 Controller
✔ 所有接口自动统计
✔ 日志格式统一


六、和 TraceId(链路追踪)如何配合?

如果你已经使用了MDC + TraceId(前一篇博客内容):

MDC.put("traceId", traceId);

那么这里的耗时日志会自动带上 TraceId,无需额外处理。

👉 这就是为什么:

  • TraceFilter

  • TimeCostInterceptor

要一起使用


七、进阶优化(生产环境强烈推荐)

1️⃣ 慢接口告警(非常实用)

if (cost > 1000) { log.warn("慢接口 | {} {} | {} ms", request.getMethod(), request.getRequestURI(), cost); }

2️⃣ 区分正常 / 异常请求

if (ex != null) { log.error("接口异常 | {} {} | {} ms", request.getMethod(), request.getRequestURI(), cost, ex); }

3️⃣ 只统计 Controller 方法

if (!(handler instanceof HandlerMethod)) { return; }

避免静态资源、错误页面干扰统计。


八、拦截器 vs AOP,该选哪个?

场景推荐
统计接口耗时✅ 拦截器
记录方法级别日志AOP
参数 / 返回值埋点AOP
接口级统一日志✅ 拦截器

👉HTTP 接口维度 = 拦截器最合适


九、一句话总结(面试 / 实战都能用)

接口耗时属于横切关注点,
应统一在 Spring MVC 拦截器中处理,
避免侵入 Controller 业务逻辑。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/7 1:43:14

CS配合CrossC2插件,实现MacOS/Linux上线

前言 我们知道CS原生只支持Windows上线,那么对于MacOS、Linux我们可以通过CrossC2插件实现上线下载地址:https://github.com/gloxec/CrossC2/releases我这里主要是演示上线MacOS,上线Linux是相同的,参考文章:https://…

作者头像 李华
网站建设 2026/8/7 23:27:29

4、Puppet 入门:从基础使用到主从架构搭建

Puppet 入门:从基础使用到主从架构搭建 1. Puppet 类型文档与常用资源类型 Puppet 安装后,代码中内置了类型文档,可通过 puppet describe 命令在命令行打印: puppet describe <type> [-s]若不确定某个类型是否存在,可使用以下命令获取所有可用资源类型的完整列…

作者头像 李华
网站建设 2026/8/7 16:28:05

线性代数(五)向量空间与子空间

根据课程内容&#xff0c;先补充一下置换矩阵和对称矩阵的概念。置换矩阵是用来交换矩阵行数或列数的单位矩阵&#xff0c;对于N阶单位矩阵&#xff0c;其具有N!个不同的置换矩阵。用排列组合的知识可以很容易证明&#xff1a;对于N阶单位阵&#xff0c;第一行可以有个位置可供…

作者头像 李华
网站建设 2026/8/7 19:11:00

matlab debug 调试程序

设置断点(一般在循环之前),点击运行然后点step分步运行点击Quit Debugging 退出调试 如果有改动,保存程序之后,才可以重新设置断点

作者头像 李华