接手一个 Spring Boot 2.0 的老项目,积累了不少技术债。原计划做一次大版本升级和架构迁移,最终选择全线回退,只保留了对项目有实际帮助的改动。

原计划

动手之前列了几个目标:

  1. Spring Boot 2.0 → 2.7.18 大版本升级
  2. Springfox → Springdoc 迁移(Springfox 停更已久)
  3. Jetty 配置集中化 —— 参数散落在多个位置,难以维护
  4. 性能参数调优 —— 调整连接池、线程池等核心参数
  5. 清理 XML 配置 —— 历史遗留的 <mvc:annotation-driven/> 等

想法很好,动手后才发现事情没那么简单。

过程

Springdoc 迁移:连环 Bean 冲突

这是最先碰壁的地方。Spring 文档、社区教程都说得挺简单的,但实际项目里情况不同:

1
2
3
4
5
6
7
8
// 问题1:@EnableAsync 冲突
// 主类 + AsyncConfigurer 各触发一次

// 问题2:@EnableTransactionManagement 冲突
// 主类 + JpaConfig 各一份

// 问题3:mvcContentNegotiationManager 冲突
// <mvc:annotation-driven/> 和自动配置抢注册

一个接一个修,每修一个站起来一个。

Spring Boot 升级:收益不明确

升到 2.7.18 本身做得通,但想想收益是什么?新版本特性当前业务用不上,回归测试成本却不小。

而且 2.0 → 2.7 跨度太大,中间牵涉到 Spring Cloud 版本对齐、配置项路径变化、废弃 API 替换,牵一发而动全身。

为什么最终选择回退

技术和业务一样,做决定的核心不是”能不能做”,而是”现在应不应该做”。

  • Springfox 停更,但它能跑
  • 2.0 够用,升级带来的风险大于收益
  • 用户不关心你用的是哪个版本

最终决定:全线回退到最初架构。

保留的改动

全线回退之后,只保留了两样:

1. Jetty 配置集中化

原本 Jetty 的线程池、队列等参数分散在不同位置,改一个参数要翻半天。全部提取到一处统一管理,可读性和可维护性都提升了。

2. 整体性能配置优化

模块 调整内容
Jetty 线程池 核心线程数、最大线程数、队列容量、超时
Druid 连接池 池大小、空闲回收、超时
Redis 连接池 池大小、超时、重试策略
Session 管理 配置优化

参数不是随便堆的,基于实际业务的压测数据来定。

最终状态

1
2
3
4
5
6
Spring Boot:  → 2.0(回退,保持原样)
Springfox: → 保持原样
Springdoc: → 放弃迁移
XML 配置: → 保留不动
Jetty 配置: ✅ 集中管理
性能参数: ✅ 全面优化

小结

这次迭代最大的收获不是完成了什么,而是学会了及时止损。

选回退和选继续推进一样,都是一次有意识的技术决策。

有些仗打不赢就不打,把弹药留给真正值得的战场。