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

【后端】【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 业务逻辑。


http://www.jsqmd.com/news/84857/

相关文章:

  • 【大前端】【Android】一文详解Android MVVM 模式详情解析
  • 46、Asterisk VoIP系统配置指南
  • 【后端】【Java】RESTful书面应该如何写
  • 45、对等网络文件共享与网关互通性解析
  • 【微科普】Louvain 算法,附python代码,让复杂网络 “自己抱团”!
  • 运输层核心总结
  • HarmonyOS 关系型数据库 RDB 数据持久化(ArkTS)实战:建库建表、CRUD、事务、FTS、性能优化,一篇搞懂
  • Cesium快速入门20:Primitive的外观设置Appearance
  • Cesium快速入门19:Entity折线材质
  • 【Codeforces】【Div2】CF 2176
  • Lonsdor K518 PRO FCV Expert License Activation for BMW, MINI, Rolls-Royce Key Programming
  • 应用层复习总结
  • 构建下一代AI算力基石 CANN ops-transformer算子仓库的生态价值与设计哲学
  • STM32F103C8T6 + SI24R1实现2.4G通信
  • 昇腾Ascend C Add算子开发实战-从理论到代码的完整构建
  • AFE为何物
  • 实验实验实验实验。
  • 代码随想录算法训练营第三十三天:零钱兑换,完全平方数,单词拆分
  • 令人“悲哀”的 C# 游戏生态 —— 主流引擎支持现状与现实困境
  • 单车慢跑中的节奏建议
  • 工具分享:彻底解决Docker拉取慢/超时,解放双手!自动测速优选配置镜像源 代理切换脚本
  • CS配合CrossC2插件,实现MacOS/Linux上线
  • 1、掌握 Puppet 4:高效管理 IT 基础设施的秘诀
  • 无需运动恢复结构(SfM)的层级训练三维高斯溅射(3D Gaussian Splatting)
  • 2、初探Puppet清单编写
  • 3、编写首个Puppet清单指南
  • 前端工程师必看:AI+前端+A/B测试 实战指南(小白友好版)
  • 4、Puppet 入门:从基础使用到主从架构搭建
  • Notepad++紧急更新,且是两个版本,究竟修复了什么
  • 5、Puppet 主节点与代理节点:全流程解析与性能优化