Administrator
发布于 2026-01-30 / 44 阅读
1

Spring Boot 4 迁移评估:值不值得现在动

一月的架构评审上,有人提议全量升 Spring Boot 4

开年第一次架构评审,议题是把公司 47 个 Java 服务统一升到 Spring Boot 4。提这个的人理由很充分:Boot 3.5 的 OSS 支持快到期,Spring AI 2.0 的里程碑版本已经明确要求 Boot 4 基线,晚动不如早动。

我当时没表态,说先让我做两周评估。这篇是评估的结论和过程。结论先说:我们最终没有全量升,只动了 9 个服务,剩下的排到下半年按业务节奏走。

先看基线要求到底变了什么

Boot 4.0 挂在 Spring Framework 7 上,硬门槛有三条:

Boot 3.5Boot 4.0
最低 JDK1717(推荐 21+)
Spring Framework6.27.0
Servlet 容器Tomcat 10.1 / Jakarta EE 10Tomcat 11 / Jakarta EE 11
Jakarta 命名空间jakarta.*jakarta.*(EE 11 版本)
Hibernate6.67.x

从表格看,JDK 17 这条对我们最友好——比想象中宽松,Boot 4 并没有像传言那样要求 JDK 21。我们内部的 JDK 分布是:

JDK 8     :  11 个服务   (老交易链路,2019 年前的遗留)
JDK 11    :   9 个服务
JDK 17    :  21 个服务
JDK 21    :   6 个服务   (2024 年后新建,主要是 AI 平台)
JDK 25    :   0 个服务

也就是说 20 个服务连 Boot 4 的最低门槛都没到,其中 11 个还停在 JDK 8。这部分如果升 Boot 4,等于把「JDK 8 → 17」和「Boot 2.x → 4.0」两件事绑在一起做,风险叠加。

与 Spring AI 的绑定关系才是真问题

单纯为了「不落后」去升 Boot 4,我是不赞成的。真正让我们必须动的是 Spring AI。

我们 AI 平台用的是 Spring AI 1.x,踩过一个很实际的坑:1.x 的 Advisor 链在流式响应下拿不到完整的工具调用记录,做审计和成本归因很别扭。Spring AI 2.0 重构了这块。但 2.0 的里程碑版本 POM 是这样的:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-bom</artifactId>
    <version>2.0.0-M2</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

拉下来看依赖树,2.0-M2 的 spring-ai-corespring-context 的要求是 7.0.x。Spring AI 2.0 和 Spring Framework 7 是绑死的,而 Framework 7 意味着 Boot 4。所以「想用 Spring AI 2.0 的新特性」和「升到 Boot 4」是同一件事,不能拆。

反过来,继续留在 Spring AI 1.x 是可行的:1.x 支持 Boot 3.x,官方还在维护。我们评估下来,1.x 能满足 90% 的需求,缺的 10%(审计链路、结构化输出的流式支持)可以用自己写的 Advisor 补。

这条判断直接决定了决策口径:AI 平台的新服务上 Boot 4,存量 AI 服务留在 1.x 不动

拿一个中等服务试跑,看看实际要改什么

我挑了「文档解析服务」做试点:1.8 万行代码,Boot 3.4 + JDK 21,依赖里有 MyBatis-Plus、Redis、Elasticsearch、RabbitMQ。这个服务复杂度中等,依赖也不算冷门,有代表性。

第一天改完 POM 编译,报了 34 个错。分一下类:

问题类型数量修复方式耗时
第三方 starter 未适配 Boot 4122 个换版本,1 个换库,9 个等升级2.5 天
Hibernate 7 查询行为变更8改 HQL / Criteria1 天
配置属性重命名6改 yml0.5 天
spring.factories 自动装配失效4迁移到 AutoConfiguration.imports0.5 天
Actuator 端点路径变化2改监控脚本0.5 天
测试相关(Mockito / 容器)2改测试基类0.5 天

最耗时的是第三方 starter。具体来说,我们用的动态数据源 starter 和某个内部封装的 MQ starter 都还没发 Boot 4 兼容版。这就是生态迁移里最难受的部分——你的代码可以改,别人的包你改不了。动态数据源那个最后换成了官方的 AbstractRoutingDataSource 自己实现,多花了 1.5 天;MQ starter 是内部包,我们自己发了个新版本。

还有个隐蔽的报错,排查了很久:

Caused by: java.lang.IllegalArgumentException:
  Could not find class [org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration]
    at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:338)
    at ...spring.factories: line 7

原因是我们一个二方库里还留着老的 META-INF/spring.factories,里面写了 Boot 2 时代的自动配置类全限定名。Boot 4 已经完全不看 spring.factories 里的 EnableAutoConfiguration 键了,但这个二方库的 jar 是通过 SpringFactoriesLoader 自己加载的,走到 ClassUtils.resolveClassName 才炸。改造方式:

// 删掉 META-INF/spring.factories,改成
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.internal.starter.mq.MqAutoConfiguration
com.internal.starter.trace.TraceAutoConfiguration

Hibernate 7 那 8 个错里有 5 个是同一个原因:query.setFirstResult() 配合 JOIN FETCH 时的分页行为变了,Hibernate 7 不再自动在内存里去重,会直接抛异常。以前是静默处理,现在报错,其实是好事,但存量代码得改。

成本算出来是多少

试点服务总共花了 5.5 人日(含回归测试和灰度观察)。按这个基准,我把 47 个服务分了三档:

档位服务数特征单服务预估合计
A 档:现在就升9JDK 21 + Boot 3.4/3.5 + 依赖干净 + 需要 Spring AI 2.04~6 人日约 45 人日
B 档:下半年随需求升17JDK 17 + Boot 3.x,业务还在迭代5~8 人日约 110 人日
C 档:暂不动21JDK 8/11,或长期无变更的稳定系统15+ 人日315+ 人日

C 档里有 6 个服务是「一年改不到两次」的老系统,运行了五六年没出过事。给它们做 Boot 4 迁移,收益接近零,风险是实打实的。我的建议很直接:这些服务不动,等到它们因为业务原因必须重写的时候再说

全量迁移的总代价是 470+ 人日,按我们团队 8 个人算,等于两个半月的产能全押进去,这期间业务需求全停。这个账在评审会上摆出来,全量升的方案就没再通过。

顺带测的性能:没有什么惊喜

迁移的价值里,性能通常是最容易被吹的一部分。我们的实测数据比较平淡,但值得记下来,免得有人拿「性能提升」当迁移理由。

同一个服务,Boot 3.5 + JDK 21 vs Boot 4.0 + JDK 21,压测 QPS 1200 持续 20 分钟:

指标Boot 3.5Boot 4.0变化
启动耗时8.4s7.9s-6%
堆占用(稳态)1.42 GB1.39 GB-2%
P99 延迟186ms181ms-3%
吞吐(QPS)1,2401,261+1.7%
CPU52%52%持平

基本持平。启动快了 0.5 秒是因为自动装配的索引方式变了(AutoConfiguration.imports 比扫 spring.factories 快),但这 0.5 秒对我们的长驻服务没什么意义。

真正有性能差异的是 AOT 和原生镜像那条路,但我们的服务是长驻的,用不上。我们只有一个 Serverless 函数试过 GraalVM:冷启动从 2.4 秒降到 180 毫秒,内存从 380 MB 降到 62 MB。这个数字很诱人,但它只对 Serverless 场景有意义,而且构建时间从 40 秒涨到 4 分 20 秒,CI 成本明显上升。

试点的回滚预案

做任何迁移之前都要想好怎么退回来,这次因为改动全在编译期,回滚其实很简单——改回 POM 版本号重新发布就行。但有两个地方是单向的:

  1. AutoConfiguration.imports 的改造。改完之后 Boot 3.x 也能读这个文件,所以是兼容的,可以放心改;
  2. Hibernate 7 的查询改写。这部分改了 HQL 和 Criteria 的写法,回到 6.6 可能行为不一致。我们的做法是在改之前把每个改动点都写了对应测试,回滚时逐个验证。

灰度方式是按流量比例:先 5% 观察 24 小时,关键指标(错误率、P99、GC、业务成功率)全部正常再全量。我们在 5% 阶段发现了一个问题——Actuator 的 /health 响应结构变了一点,我们的旧监控脚本解析失败,导致健康检查误报。这类问题在 5% 流量下暴露出来,代价很小。

最终怎么定的

三条原则,写进了我们的技术雷达:

  • 新服务一律 JDK 21 + Boot 4.0,从 2026 年 2 月起生效。新建服务没有历史包袱,直接用新基线,成本几乎为零;
  • AI 平台相关服务优先升,因为我们确实要吃 Spring AI 2.0 的红利。这部分 9 个服务排进 Q1,两个月内完成;
  • 存量服务按「是否被业务需求触碰」决定。一个服务如果今年有大的功能迭代,就在迭代里顺带升级,摊薄成本;没有迭代计划的,不动。

另外定了一条硬规则:JDK 25 暂时不作为生产基线。它在 2025 年 9 月已经是 LTS 了,虚拟线程、结构化并发、紧凑对象头这些我们都在测,但生产基线统一锁 JDK 21,等 25.0.2 之后再评估。这不是保守,是我们吃过亏——2024 年我们第一时间上了 JDK 21.0.0,被一个 G1 的 bug 咬了半个月。

就写到这。如果哪天你也被《Spring Boot 4 迁移评估:值不值得现在动》里同一个坑绊住,回来翻这篇,能省半小时。

参考