news 2026/5/16 4:48:54

【后端】【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/5/14 1:19:13

46、Asterisk VoIP系统配置指南

Asterisk VoIP系统配置指南 1. Asterisk模块加载建议 对于Asterisk新手而言,由于其模块较为“挑剔”,为避免出现问题,最好自动加载 modules 目录下的所有模块。随着对Asterisk的熟悉程度增加,可以使用 noload 指令明确告知Asterisk不加载不需要的模块。 2. Asterisk…

作者头像 李华
网站建设 2026/5/14 1:22:10

14、充分利用语言的完整工具集

充分利用语言的完整工具集 在配置管理中,我们常常需要高效地管理和分配资源。Puppet 提供了一系列强大的功能,让我们可以更灵活地处理各种资源。下面将详细介绍 Puppet 中的资源标签、资源导出与导入、资源参数覆盖以及资源默认值等重要特性。 资源标签的使用 Puppet 会隐…

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

AI算力:驱动智能时代多元计算与高效调度的基础

各行各业正被人工智能发展浪潮以前所未有的力道进行重塑,而且支撑其运作的底层基础是算力。换种通俗易懂的说法,AI算力是指用来执行人工智能算法,对海量数据予以处理以及解析所需要的计算能力。它不是一项简单的硬件指标,而是包含…

作者头像 李华
网站建设 2026/5/14 1:20:18

AI知识库的构建:从数据采集、处理到高效检索的全流程解析

AI知识库的搭建以及运用属于一项具备系统性的工程项目,它的关键之处在于怎样以高效且精准地把领域方面的知识给予人工智能系统,从而让其拥有专业领域范畴内的认知以及推理能力。这个进程一般被称作“知识库投喂”或者“知识灌注”,它并非仅仅…

作者头像 李华
网站建设 2026/5/14 1:25:24

Git工作流程指南: 基础 Git 操作

基础 Git 操作 仓库初始化 # 创建新的仓库 git init# 克隆已经存在的仓库 git clone <repository_URL>文件操作 # 将文件添加到暂存区&#xff08;Staging Area&#xff09; git add <file_name> # 添加单个文件 git add . …

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

Modelsim联合高云ip仿真流程

一、改变工作器件库打开modelsim&#xff0c;调整工作器件库&#xff1a;modelsim --> File --> Change Directory --> 选择modelsim的安装目录C:\modeltech64_10.7&#xff08;我装在C盘&#xff09;&#xff0c;点击选择文件夹&#xff0c;如下图。二、创建libirary…

作者头像 李华