
在过去两年里,团队里有一个现象很典型:
Spring Boot 3.0 已经在生产跑得很稳,但大家都不太愿意再动它。
从结果看,系统没有明显问题;
从风险看,技术债却在不断累积。
这篇文章并不是"追新版本"的经验总结,而是基于一次真实的 Spring Boot 3.0 → 3.5
升级实践,梳理我们为什么决定升级、升级过程中真正发生了哪些变化,以及这些变化在实际项目中带来的价值。
一、为什么从 3.0 升到 3.5
1. 3.0 的问题不在"能不能用",而在"是不是终态"
Spring Boot 3.0 对我们来说,是一次架构断代式升级:
- 从 Java EE 全面切换到 Jakarta EE
- 最低 Java 版本直接拉到 17
- Spring Framework 6.x 成为唯一底座
但客观讲,3.0 更像一个"起点版本",而不是成熟生产线。
在实际使用中,我们逐渐遇到一些共性问题:
- 启动时间偏长,对弹性扩缩容不友好
- 可观测性体系需要大量手动拼装
- 某些配置问题只在运行期才暴露
- Spring Security 的默认行为不够"显性",排查成本高
这些问题并不致命,但它们共同指向一件事:
3.0 能用,但工程体验并不理想。
2. 官方版本节奏给出的信号很明确
如果把 Spring Boot 3.x 拆开来看,其实节奏非常清晰:
- 3.0 / 3.1:完成断代迁移
- 3.2 / 3.3:补齐兼容性与稳定性
- 3.4 / 3.5:进入成熟生产主线
从 Spring Framework 的角度看也是一致的:
- 3.0 基于 Spring Framework 6.0
- 3.2/3.3 基于 6.1
- 3.5 基于 6.2,生态成熟度明显更高
换句话说,如果 3.0 是"迈过门槛",那 3.5 才是工程能力真正释放的阶段。
二、3.0 到 3.5,核心变化到底体现在哪里
升级不是版本号的变化,而是底层能力的变化。我们重点感知到的有四个方面。
1. 启动与运行性能:不是"极限优化",而是更稳定
在 3.0 时期,系统启动慢并不是偶发现象,尤其在容器环境下更明显。
升级到 3.5 后,我们在不做任何业务代码改动的前提下:
- 启动时间整体降低约 20%--40%
- 冷启动波动明显收敛
- 初始化阶段日志更可读,问题更容易定位
这并不是某一个"神奇优化",而是来自:
- Bean 初始化路径优化
- AOT 相关能力逐步工程化
- 反射与条件装配的整体收敛
这种优化对单次启动可能不"惊艳",但对云原生场景非常关键。
2. 可观测性:从"外接能力"变成"内建能力"
在 3.0 中,我们的可观测性方案是典型的"拼装式":
- Micrometer + Prometheus
- 日志与 Trace 关联依赖额外适配
- 链路数据结构不统一
升级到 3.5 后,一个明显的变化是:
Observation 成为 Spring 的核心抽象,而不是附加能力。
实际效果是:
- Metrics / Tracing / Logging 语义统一
- OpenTelemetry 接入成本显著下降
- 不再需要在业务层反复做埋点约定
这对长期维护系统来说,价值远高于单纯的性能提升。
3. 配置与失败策略:更"严格",但更安全
3.5 对配置绑定的校验明显更严格:
- 类型不匹配会直接阻断启动
- 缺失关键配置不再"悄悄使用默认值"
一开始这确实带来了一些启动失败,但从结果看:
问题暴露得越早,系统就越安全。
这在生产系统中是一个明显的正向变化。
4. 安全模型:从被动修补到主动防护
Spring Security 在 3.x 后期的默认策略更加明确:
- 默认更安全
- 行为更可预测
- 配置意图更清晰
这减少了"升级后突然 403"的不可控风险,也让安全治理更偏向体系化,而不是事后补救。
三、我们选择的升级路径
在团队内部,我们讨论过两条路线:
- 阶梯式升级(3.0 → 3.2 → 3.5)
- 一步到位升级(3.0 → 3.5)
最终,我们选择了第二种。
原因很现实:
- 项目已完成 2.x → 3.0 的断代
- JDK 已统一在 17
- 自动化测试覆盖核心路径
- 继续停留在中间版本,收益有限
我们更愿意一次性承受集中问题,而不是分阶段反复折腾。
四、一个真实项目的升级实践
1. 项目背景
- 中台型服务,偏数据与流程编排
- Spring Boot 3.0.2
- 部署在 Kubernetes
- 日均调用量百万级
2. 升级过程中的关键问题
(1)Jakarta 包残留
虽然 3.0 已迁移过一次,但仍有少量依赖间接引用了 javax.*,升级后直接暴露。
解决方式很直接:
- 全量依赖树检查
- 禁止手动覆盖 Spring 相关版本
(2)Spring Security 行为变化
部分接口在升级后返回 401/403,与预期不一致。
最终发现问题并不在代码,而在于:
- 默认安全策略更严格
- 原先"隐式放行"的路径需要显式配置
(3)JSON 与时间序列化差异
个别接口返回的时间字段精度发生变化,影响了前端解析。
这个问题并不复杂,但提醒我们:
升级一定要覆盖端到端测试,而不仅是接口可用。
3. 升级后的实际收益
从结果看,升级带来的变化是明确的:
| 维度 | 升级前(3.0) | 升级后(3.5) |
|---|---|---|
| 启动时间 | 启动耗时较长 | 启动时间降低约 20%--40% |
| 内存表现 | 波动偏大 | 峰值更稳定 |
| 可观测性 | 依赖外部拼装 | 原生 Observation 体系 |
| 安全治理 | 以被动修复为主 | 默认策略更安全 |
更重要的是:
团队对 Spring Boot 3.x 的信心明显增强了。
五、一些个人总结
这次升级让我最大的感受是:
Spring Boot 3.5 并不是"最新版本",而是"成熟版本"。
升级本身并不是目的,真正的价值在于:
- 是否建立了可控升级能力
- 是否减少了长期技术风险
- 是否让系统更符合未来 3--5 年的演进方向
如果你的项目还停留在 3.0.x,我的建议很明确:
可以不急,但不应该无计划地停留。
结语
从工程角度看:
3.0 是门槛,3.5 才是生产力。
升级不是版本号的游戏,而是一次工程能力的自检。