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

Spring MVC 接口匹配性能优化:.do后缀引发的性能瓶颈分析

这个分析过程是我发在群里的,然后再让AI整理成文章的(有AI之后,写文章都变懒了😂),请勿介意

在一次线上系统性能排查中(其他项目组的),发现一个看似平常但影响深远的问题——**前端请求接口时携带了 .do 后缀(如 /fileType/findFileList.do),而后端 Spring MVC 的 @RequestMapping 中并未显式包含 .do,导致请求处理过程中触发了大量不必要的路径匹配逻辑,最终造成接口响应延迟高达 78 毫秒

项目是一个老的技术栈,Tomcat7SpringMVC项目,就是JSP那一套,所以Spring版本也不是很高


一、背景介绍

当前系统是一个基于 Spring MVC 的 Web 应用,使用 Tomcat 作为容器。前端通过 Ajax 请求后端接口,所有请求均以 .do 结尾,例如:

https://127.0.0.1/project/fileType/findFileList.do

而在后端控制器中,对应的映射路径为:

@RequestMapping("/fileType/findFileList")
public ResponseEntity<?> findFileList(...) { ... }

即:前端带 .do,后端不带 .do

系统共有约 9500个 URL 映射,全部注册在 AbstractHandlerMethodMapping.MappingRegistry#urlLookup 中,这是一个 Map<String, List<HandlerMethod>>,用于快速查找匹配的处理器方法。


二、问题复现与定位

1. 线程栈分析

我们在生产环境捕获到多个线程阻塞在以下调用链中:

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod
→ addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request)for (T mapping : mappings) { getMatchingMapping(mapping, request); }

关键点在于:

  • lookupPath/fileType/findFileList.do
  • mappingRegistry.getMappingsByUrl(lookupPath) 返回 null(因为注册的是不带 .do 的路径)
  • 进入 addMatchingMappings() 方法,遍历整个 keySet()(好几千个,不是urlLookup中的数量),逐个进行模式匹配

2. 性能对比实测

请求方式响应时间是否命中 urlLookup
/fileType/findFileList.do78ms❌ 否
/fileType/findFileList1ms✅ 是

💡 结论:当请求路径带有 .do 且未在 @RequestMapping 中声明时,Spring MVC 无法直接命中 urlLookup,只能通过全量遍历 + 模式匹配的方式寻找匹配项,导致性能急剧下降。


三、Spring MVC 路径匹配机制详解

1. 正常流程(直接命中)

lookupPath/fileType/findFileList的时候

List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {addMatchingMappings(directPathMatches, matches, request);
}
  • lookupPathurlLookup 中存在,则直接返回对应 Handler,无需遍历。

2. 回退机制(全量扫描)

lookupPath/fileType/findFileList.do的时候getMappingsByUrl() 返回 null 时,执行:

addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
  • 遍历所有已注册的路径
    在这里插入图片描述

3. 匹配逻辑细节

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {for (T mapping : mappings) {if (match = getMatchingMapping(mapping, request)) {matches.add(new Match(match, this.mappingRegistry.getMapping(mapping)));}}
}

其中 getMatchingMapping() 内部会调用 AntPathMatcher.match(),涉及正则表达式匹配、通配符解析等操作,每次调用都消耗一定 CPU 资源。


四、为何 .do 能“自动”匹配?

尽管后端没有写 .do,但前端请求仍能成功访问,是因为:

Spring MVC 默认开启后缀匹配(suffix pattern matching)

具体体现在 RequestConditionHolderPatternsRequestCondition 的实现中,当启用 useSuffixPatternMatch=true(默认开启)时,Spring 会对请求路径进行后缀剥离,尝试匹配无后缀版本。

例如:

  • 请求:/admin/sysNotice/getMyNotice.do
  • 实际匹配:/admin/sysNotice/getMyNotice

因此虽然不能直接命中 urlLookup,但仍可通过 addMatchingMappings 找到目标 Controller。

但这带来了严重的性能代价!


五、性能瓶颈可视化证据

1. 线程栈截图分析

在这里插入图片描述
在这里插入图片描述

红框部分显示大量调用 getMatchingCondition()getMatchingPattern()doMatch() 等方法,正是 addMatchingMappings 中的循环匹配过程。

2. Arthas 性能监控结果

在这里插入图片描述
在这里插入图片描述

  • .do 的请求耗时接近 80ms
  • 不带 .do 的请求仅需 0.7ms

说明:是否直接命中 urlLookup 决定了性能差异


六、解决方案建议

方案一:在 @RequestMapping 中显式添加 .do(推荐)

@RequestMapping("/fileType/findFileList.do")
public ResponseEntity<?> findFileList(...) { ... }

✅ 优点:

  • 直接命中 urlLookup,避免全量扫描
  • 改动小,不影响现有业务逻辑
  • 不影响静态资源访问

❌ 缺点:

  • 接口风格略显冗余
  • 可能需要批量修改注解

👉 适合大多数场景,是最安全、最高效的方案。


方案二:配置 Tomcat 将非 .do 请求也交给 Spring MVC 处理

修改 web.xml 或使用 DispatcherServleturl-pattern 配置:

<servlet-mapping><servlet-name>SpringMvc</servlet-name><url-pattern>/</url-pattern> <!-- 匹配所有请求 -->
</servlet-mapping>

⚠️ 注意事项:

  • 静态资源(JS/CSS/IMG)也会被 Spring MVC 拦截,可能导致 404 或异常
  • 需配合 ResourceHttpRequestHandlerDefaultServlet 解决静态资源问题
  • 可能引入额外的过滤器干扰

👉 风险较高,需谨慎测试,建议仅在可控环境下使用。


七、最佳实践建议

  1. 统一接口命名规范:明确是否使用 .do 后缀,并在整个项目中保持一致。
  2. 优先使用精确匹配:尽量让请求路径与 @RequestMapping 完全一致,避免依赖后缀匹配。
  3. 监控关键路径性能:利用 Arthas、SkyWalking 等工具监控 getHandler() 方法耗时。
  4. 避免大规模路径匹配:若系统有上万个接口,应避免频繁触发全量扫描。

八、总结

关键点说明
问题本质请求路径含 .do,但注册路径不含,导致无法命中 urlLookup
性能影响触发全量路径扫描,耗时从 1ms 升至 78ms
根本原因Spring MVC 后缀匹配机制虽能工作,但效率低下
解决方案✅ 显式添加 .do@RequestMapping;或 ⚠️ 修改 url-pattern
最佳实践统一接口风格,避免依赖模糊匹配
http://www.dtcms.com/a/517266.html

相关文章:

  • ps图做ppt模板下载网站有哪些创意设计生活用品
  • Windows API 文件结构与功能分类详解
  • 网站建设费开票收候开在哪个类别里做用户名验证的网站服务器
  • 常熟制作网站的地方wordpress解决速度慢
  • 自己可以做网站推广吗最新的新闻 最新消息
  • Redis不停机升级5.0.3->8.0.4
  • 做网站语言排名2018网站开发的方法和步骤
  • 网页设计入门首先要学什么企业网站优化与推广
  • 抓住园艺消费升级!亚马逊卖家如何从“卖单品”升级为“做品牌”
  • FPGA-ZYNQ学习对BD的保存与应用
  • 博罗县建设局网站免费好玩的网页游戏
  • 强化运动控制领域布局,杰美康机电授权世强硬创代理
  • 容器适配器、关联容器的相关算法题目
  • 微网站的好处优秀产品设计公司哪家好
  • 树莓派Pico 2W 开发环境搭建
  • 零基础从头教学Linux(Day 54)
  • Dexmal原力灵机发布Dexbotic,从此具身智能研发有了“加速器”
  • 电子商务网站建设步网页打不开验证码图片
  • 怎样做自己的微商网站个人网站备案建设方案书
  • 《用于触觉感知的电阻式传感器技术的最新进展》IEEE SENSORS 2022综述文章解读
  • Redis vs RabbitMQ 对比总结
  • 初识C语言11.数据在内存中的存储
  • 操作系统存储管理核心知识点与例题详解
  • 人工智能风险与伦理(3)
  • the scientist and engineer‘s guide to DSP:5 Linear Systems
  • 近一个月的技术问题总结
  • 360网站推广官网怎么优化网站开发的项目内容
  • 用多工具组合把 iOS 混淆做成可复用的工程能力(iOS混淆|IPA加固|无源码混淆|Ipa Guard|Swift Shield)
  • 丽水网站建设企业wordpress空白页面模板下载
  • 如何推广企业官方网站内容微信公众号推广链接