一、要不要迁:时间线先把这个问题定死
Spring Boot 3 的入门认知站内已有《Spring Boot 框架入门》打底,这一篇假设你已经写过 3.x 项目,只回答一个问题:3.x 到 4.x 这一跳,带来什么、破坏什么、怎么迁。先把时间线摊开,因为”要不要迁”不是靠感觉,是靠支持周期到期日定死的:
| 时间 | 事件 | 对”要不要迁”的含义 |
|---|---|---|
| 2025-05 | Spring Boot 3.5 发布(3.x 最后一条线) | 3.x 进入收尾,新特性不再来 |
| 2025-11-13 | Spring Framework 7.0 GA | 新一代基座就位 |
| 2025-11-20 | Spring Boot 4.0 发布(Maven Central) | 迁移目标出现 |
| 2026 年内 | 3.x 各线开源支持陆续到期;4.0 的开源支持到 2026-12-31(官方支持页口径) | 2027 年还停在 3.x = 拿不到开源补丁,迁移从可选项变成到期事务 |
| 撰写时(2027-04) | 4.x 的后续维护线(4.1.x 起)为当前活跃线 | 迁就迁到当时的最新补丁线,不要停在 4.0.0 |
再看运行基线,好消息是这次没有强制搬家:
| 基线项 | Spring Boot 3.x | Spring Boot 4.x | 你要动吗 |
|---|---|---|---|
| Java | 17 起 | 17 起(没升基线),Java 25 为一等支持(最新 LTS) | 不强制;生产建议顺势上 25 |
| Jakarta EE | EE 10 / Servlet 6.0 | EE 11 / Servlet 6.1 | 内嵌容器随版本走(Tomcat 11),一般无感 |
| Jackson | 2.x 默认 | 3.x 默认(包名 tools.jackson) | 要动,见第四节 |
Java 基线留在 17 是这代升级最大的善意:上一跳(2.x 到 3.x)最疼的 javax 到 jakarta 迁移这代没有对应物,多数项目的工作量集中在”模块化拆分的连锁反应 + 弃用清理”两件事上——正好是下两节的主角。
二、模块化重构:带来什么
Boot 4 最大的结构性变化是把那块”什么都有”的 spring-boot-autoconfigure 巨型 jar 拆成了按技术粒度的一组模块——Web 服务器、数据访问、缓存、消息,各有各的自动配置模块(如 spring-boot-web-server-autoconfigure 这类命名)。官方博客的说法是”模块化 Spring Boot”,注意这里的”模块”指的是 jar 拆分,不是 JPMS(Java 平台模块系统),别被词吓到。
对普通应用开发者,这个拆分大部分被 starter 屏蔽了:starter 会替你声明它需要的自动配置模块,多数应用改个版本号就能编译。它真正改变的是两件事。第一,装配更可解释。以前排查”这个 Bean 从哪来的”要在一个巨型 jar 里翻;现在按技术分家,条件评估报告与源码组织对得上,排障时少一层迷雾。第二,依赖图更诚实。以前一个传递依赖就能把整块自动配置拖进来;拆分后,用了什么就声明什么,类路径上少一堆”不知道为什么在”的候选配置。
三、破坏什么:三类高频断裂点
断裂点一:spring.autoconfigure.exclude 里的类搬家了
3.x 里按完整类名排除自动配置的配置项,在 4.0 里会因为类迁入新技术模块而失效——失效方式是启动期找不到类,属于”一启动就炸”的友好型破坏。修法是把每个条目按官方迁移指南的对照表重定位;OpenRewrite / Moderne 提供了自动重定位这类条目的迁移配方,条目多的项目值得先跑一遍再人肉核对:
# application.yml / application.properties
1. 3.x 写法:按旧 FQN 排除
1. spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
1. 4.0:类已迁入按技术命名的新模块,完整类名以官方迁移指南对照表为准,逐条重定位
断裂点二:自定义 starter 与三方 starter
这是拆分冲击最大的人群。3.x 时代写自定义 starter 有个普遍的隐性假设:classpath 上有 spring-boot-autoconfigure 这块”大杂烩”,自己的自动配置类随便引用其中任何类都编译得过。4.0 里这个假设作废——每个 starter 需要显式声明并依赖自己所属技术的那块自动配置模块。给团队内部框架的维护者列一张对照:
| 关注点 | 3.x 的习惯 | 4.0 要做什么 |
|---|---|---|
| 依赖声明 | 依赖 spring-boot-autoconfigure 一把梭 | 按用到的技术显式依赖对应的自动配置模块(Web、JDBC、Flyway 各归各) |
| 自动配置注册 | META-INF/spring 下的 imports 声明 | 注册机制延续,但类的所在 artifact 变了,打包关系要重排(以官方迁移指南为准) |
| 使用方的排除项 | 旧 FQN | 发布迁移说明,告知使用方同步重定位 exclude 条目 |
| 验证方式 | “编译过就行” | 必须在一个真实 4.x 应用里做装配冒烟:自动配置缺失在编译期不报错,只在运行期静默缺失 |
反模式一:靠传递依赖”顺带”拿到自动配置。4.0 下这是最典型的静默故障源——starter 编译通过、发布成功、使用方启动也成功,只是某个该自动装配的 Bean 没了,最后以 NoSuchBeanDefinition 的形式在离根因很远的地方爆出来。自定义 starter 在 4.x 下的纪律是:用哪块、声明哪块、测哪块。
断裂点三:3.x 生命周期内的弃用一次性全删
官方迁移指南的口径很硬:3.x 期间标记弃用的类、方法、属性,在 4.0 里全部移除,没有缓冲期。这意味着”在 3.5 上能编译过”不等于”在 4.0 上能编译过”——那些年你忽略掉的弃用告警,这一版集中结账。几个社区实测里高频命中的具体项:
| 弃用/移除项 | 3.x 行为 | 4.0 行为 | 迁移动作 |
|---|---|---|---|
| 全部 3.x 弃用 API | 标记弃用仍可用 | 移除 | 先在 3.5 上开 -Wdeprecation 或跑 OpenRewrite 清零告警,再升版本 |
| H2 控制台 | classpath 有 H2 即随 Actuator 暴露 | 不再默认包含 | 开发环境需要的话按迁移指南显式补模块与配置 |
| Spring Security 旧 DSL | 可用(带弃用告警) | 移除,要求显式配置 | 按新版安全配置清单逐段重写;迁移后必须做行为回归,见第七节 |
| Jackson 2 默认装配 | 默认 2.x | 默认 3.x,包名 com.fasterxml.jackson 改为 tools.jackson | 两步走,见下节 |
四、Jackson 3:唯一建议”分两步”的项
Jackson 3 换了坐标(tools.jackson 命名空间)也改了一批默认行为,是这次升级里改动面最广的单项。好消息是官方明确支持 Jackson 2 与 3 共存,所以迁移策略可以解耦:先升 Boot 4、暂留 Jackson 2(显式声明 ObjectMapper 与消息转换器 Bean),让应用先跑起来;再单独开一个分支做 import 全局替换与默认行为对齐。一步到位当然也行,但把”框架升级”和”序列化行为变更”两件高风险事叠在同一次发布里,出问题时你分不清是哪边引入的。
五、内建 retry:少一个依赖,多一套语义
这代升级里少见的”净赚”项:Spring Retry 作为独立项目并进了 Framework 7 的 spring-core,@Retryable、RetryTemplate 成为框架原生能力,不用再单独引入 spring-retry 依赖,还附赠了并发闸门 @ConcurrencyLimit;注解用在返回 Mono/Flux 的方法上时,会自动改用 Reactor 的 retry 算子而不是阻塞式重试。示例:
@Service
public class InvoiceClient {
// 注意语义:maxRetries 是"重试次数",3 表示首次调用之外再试 3 次
// 旧 spring-retry 的 maxAttempts 是"总尝试次数",迁移时数字不能照抄,见下表
@Retryable(maxRetries = 3)
public Invoice fetch(String id) {
// 调外部发票服务,失败由框架按注解重试
return gateway.get(id);
}
// 并发闸门:同时只放行 10 个调用,超出的快速失败
// 保护的是下游,也保护自己线程池——与 retry 同属这代的弹性能力包
@ConcurrencyLimit(10)
public void push(String id) {
gateway.notify(id);
}
}
| 维度 | 旧 spring-retry | Framework 7 内建 |
|---|---|---|
| 依赖 | org.springframework.retry:spring-retry 单独引入 | spring-core 自带,去掉依赖 |
| 注解计数语义 | maxAttempts:总尝试次数(含首次) | maxRetries:重试次数(不含首次) |
| 程序式 API | RetryTemplate | RetryTemplate(随能力一并并入 core) |
| 响应式 | 不覆盖 | Mono/Flux 返回值自动走 Reactor retry |
| 附加能力 | — | @ConcurrencyLimit 并发闸门 |
反模式二:把 maxAttempts 的值原样搬给 maxRetries。旧值 4 表示”首次 + 3 次重试”,照抄成 maxRetries = 4 就变成 4 次重试,重试压力多一轮、超时窗口变长——这种错不报编译错误,只在下游故障时的报警曲线里现形。迁移时把项目里所有手写重试计数列出来逐个换算(maxAttempts 等于 maxRetries 加一)。
六、迁移步骤清单
顺序本身是正确性的一部分,别按喜好重排:
| 步骤 | 做什么 | 为什么在这个位置 |
|---|---|---|
| 前置清障 | 升到 3.5 最新补丁;开 -Wdeprecation 清零弃用告警(或跑 OpenRewrite 的 Boot 4 配方) |
3.x 弃用在 4.0 全删;在 3.5 上清,问题定位还有旧版本上下文 |
| 依赖矩阵对齐 | 确认 Spring Cloud、三方 starter、内部 starter 都有 4.x 兼容版本,先升它们 | 任何一块没跟上,主工程升了也起不来 |
| 主工程升版本 | Spring Boot 升到当时的最新 4.x 补丁线;处理编译错误(exclude 条目重定位、自动配置类搬迁、Jackson import) | 放在清障后,编译错误的”新账”才不会和弃用的”旧账”混在一起 |
| Jackson 决策 | 暂留 2(显式 Bean)或整体切 3 | 见第四节,两步走时这步只做”声明保持” |
| 安全配置重写 | 旧 DSL 移除后的 Security 配置按新写法重写 | 放编译通过之后、回归之前,重写完必须全量回归 |
| 回归清单 | 按下一节的清单逐项过 | 升级造成的故障多数不在编译期现形 |
七、回归清单
| 回归项 | 怎么验 | 防的是哪类断裂 |
|---|---|---|
| 自动配置完整性 | 启动加 --debug 看条件评估报告;对照 3.x 时代的报告 diff 一遍 |
拆分导致的静默缺失(本文第三节) |
| Actuator 端点 | 全量端点逐个 curl,对齐暴露列表 | 模块拆分后端点装配变化 |
| 安全行为 | 匿名/认证/越权三类请求路径各打一遍(自动化测试最好) | Security 旧 DSL 移除后的默认行为变化 |
| JSON 序列化 | 关键接口的请求/响应做快照对比(空值、日期格式、未知字段策略) | Jackson 3 默认行为差异 |
| 重试与超时 | 故障注入:下游超时/报错时观测实际重试次数与间隔 | maxAttempts 到 maxRetries 的语义差 |
| 内部 starter 使用方 | 每个依赖方应用至少一次完整启动 + 核心链路冒烟 | 自定义 starter 的装配静默缺失 |
八、诚实结论
这一跳的真实工作量分布:没有自定义/三方 starter 依赖、没有重度安全定制的小服务,多数是半天级——升版本、清弃用、跑回归;有内部 starter 的团队,工作量在 starter 侧而不是应用侧,且必须为”使用方静默装配缺失”准备专门的冒烟流程,预算按天算。判断顺序建议:先盘依赖矩阵(Spring Cloud、三方、内部 starter 有没有 4.x 线),再定排期——矩阵里任何一块没有 4.x 兼容版本,你单独迁主工程就是在制造一个起不来的分支。Java 语言侧配套的语法基线变化,可参见《Java 现代语法特性》。
版本核实(以官方文档为准):Spring Framework 7.0 GA 于 2025-11-13、Spring Boot 4.0 于 2025-11-20 发布(spring.io 官方公告);Java 基线 17 未升、Java 25 一等支持(官方公告口径);4.0 开源支持至 2026-12-31(官方支持页),撰写时 4.1.x 为活跃维护线;模块化拆分(spring-boot-autoconfigure 按技术拆分、非 JPMS)、exclude 条目重定位(Moderne/OpenRewrite 迁移配方)、Jackson 3(tools.jackson,官方博客明确 2/3 可共存)、spring-retry 并入 spring-core 与 maxRetries 语义、@ConcurrencyLimit(官方博客 Core Spring Resilience Features)、H2 控制台默认不暴露与 Security 旧 DSL 移除(社区实测记录,以官方迁移指南为准)。一切以 docs.spring.io 与 GitHub 官方迁移指南为准。