news 2026/4/15 8:32:49

【后端】【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/4/11 17:31:49

51、Linux网络文件共享与Samba服务全解析

Linux网络文件共享与Samba服务全解析 在当今的网络环境中,实现文件和资源的共享是非常重要的。本文将介绍两种实现网络文件共享的技术:网络文件系统(NFS)和Samba服务。 NFS:网络文件系统 NFS是一种用于在网络上共享文件系统的协议,它允许用户在不同的计算机之间共享文…

作者头像 李华
网站建设 2026/4/14 1:04:55

运输层核心总结

运输层位于网络层之上、应用层之下,核心职责是为应用进程提供端到端的逻辑通信,屏蔽网络层的异构性和不可靠性。通过 UDP 和 TCP 两种核心协议,分别提供无连接的尽最大努力交付和面向连接的可靠交付服务,依托端口实现进程间通信的…

作者头像 李华
网站建设 2026/4/11 16:11:57

3、编写首个Puppet清单指南

编写首个Puppet清单指南 清单排序配置 Puppet的近期版本支持基于本地清单的排序方式。在 puppet.conf 配置文件中,可按如下方式配置基于清单的排序: ordering = manifest此设置在Puppet 4中为默认配置。不过,了解排序原则仍十分重要,因为在更复杂的清单中,隐式顺序难…

作者头像 李华
网站建设 2026/4/15 5:46:53

6、深入探究 Puppet:Facts、Types 与 Providers 详解

深入探究 Puppet:Facts、Types 与 Providers 详解 一、Facter 系统简介 在 Puppet 中,最初的解决方案虽然强大但成本高昂。主节点在编译过程中遇到特定表达式时需回调代理节点,编写能处理命令返回错误码的清单很费力,且 Puppet 可能变得像奇特的脚本引擎。 当使用 pupp…

作者头像 李华
网站建设 2026/4/14 11:10:03

技术创新引领产业升级:数字化转型下的企业发展新路径

在当今快速变化的全球经济环境中,技术创新已成为驱动产业升级和企业发展的核心动力。随着数字化转型的不断深入,传统行业正面临前所未有的机遇与挑战。本文将探讨数字化转型的核心要素、企业在转型过程中遇到的常见问题以及未来发展趋势,为企…

作者头像 李华