本节目标:把「从 3.x 迁到 4.x」从一堆零散改动,整理成有先后依赖、可逐条执行、可回退的步骤清单——覆盖 Java 与构建基线、依赖坐标与版本、类与注解改名、配置属性重命名、
spring.factories沿革与测试写法,并给出每一条的核实命令。
适用版本:Spring Boot 4.1.x(Java 21)
11.2 依赖与配置迁移清单
迁移最常见的失败不是「某一项不会改」,而是改的顺序错了。先改 import 再改依赖,编译器会用一屏「找不到符号」淹没你;先改配置属性再改 starter,启动时会因为自动配置没加载而静默失效。所以这一节不按「类型」罗列,而是按依赖关系排成步骤:上一步的产出是下一步的输入。
11.2.1 迁移步骤的依赖关系
先看清整张图的因果关系:
第 0 步 升到最新 3.5.x,清掉所有废弃调用
│ (不先做这步,4.x 里被移除的废弃 API 会同时爆出来)
▼
第 1 步 抬 Java 与构建插件基线(Java 17+/21,Jakarta EE 11)
│ (不先抬,后面新依赖根本编译不过)
▼
第 2 步 依赖坐标与版本(BOM 版本、starter 改名、新增 starter)
│ (依赖对了,classpath 才稳定,才能判断后续错误)
▼
第 3 步 类与注解改名(import、Jackson 3、测试注解)
│ (依赖对了,改 import 时编译器只报"真正缺的")
▼
第 4 步 配置属性重命名与移除
│ (编译通过了,才能安全地做启动期验证)
▼
第 5 步 自有自动配置 / starter 的注册文件沿革
│
▼
第 6 步 测试写法适配
为什么第 0 步必须在最前:4.x 移除了 3.x 中所有已废弃的类、方法、属性。如果你带着废弃调用直接升到 4.x,会同时面对「废弃 API 被移除」和「模块化改名」两类错误,难以区分。先在 3.5.x 上把所有废弃警告清零,4.x 的报错就只剩模块化这一条线索。
11.2.2 第 0 步:在 3.5.x 上清废弃
对照线是 Spring Boot 3.5.16(Spring Framework 6.2.19,Servlet 6.0 / Jakarta EE 10,Jackson 2,Tomcat 10.1)。本机已实测跑通 3.5.16 与 4.1.1 两条版本线。
# 用编译器警告 + 静态检查找出所有废弃调用
# -Xlint:deprecation 会把每一处废弃 API 的位置打出来
javac -Xlint:deprecation -d /tmp/out $(find src/main/java -name '*.java')
在 Maven 里可以给 compiler 插件加 <compilerArgs><arg>-Xlint:deprecation</arg></compilerArgs>。目标是把警告数降到 0——每一条废弃警告,在 4.x 里都是一条必改项。
11.2.3 第 1 步:Java 与构建插件基线
4.x 的系统要求是硬门槛,先抬到位再动依赖:
| 项目 | 3.5.x | 4.x |
|---|---|---|
| Java | 17(3.x 最低) | 17+,本系列用 21 |
| Kotlin | 1.x 系 | 2.2+ |
| GraalVM native-image | 22/23 | 25+ |
| Jakarta EE | 10 | 11 |
| Servlet | 6.0 | 6.1 |
| Spring Framework | 6.2.x | 7.x |
Maven 侧的对应动作:
<properties>
<java.version>21</java.version>
<maven.compiler.release>21</maven.compiler.release>
</properties>
如果你手动管理 Spring Framework 版本,务必升到 7.x——4.x 与 Framework 6.x 不兼容。另外 4.1 起有一处构建插件行为变更:-DskipTests 不再跳过测试的 AOT 处理,Maven 插件只认 maven.test.skip。如果你的 CI 依赖 -DskipTests 来快速打包,升级后要么改用 maven.test.skip=true,要么接受 AOT 处理的开销。
11.2.4 第 2 步:依赖坐标与版本
2a. BOM 与 starter 改名。 把 spring-boot-starter-parent 或 spring-boot-dependencies 升到 4.1.1,然后按上一节的对照表改名 starter。最容易漏的是「以前不需要 starter、现在需要」的那类:
| 技术 | 改动 |
|---|---|
| Flyway | 加 spring-boot-starter-flyway |
| Liquibase | 加 spring-boot-starter-liquibase |
2b. 依赖大版本对照。 4.0 与 4.1 的依赖版本不同,别混。下表左列是 4.0 引入的口径,右列是本机 spring-boot-dependencies-4.1.1.pom 实测口径:
| 依赖 | 4.0 起 | 4.1.1 实测 |
|---|---|---|
| Spring Framework | 7.0 | 7.0.9 |
| Spring Security | 7.0 | 7.1.1 |
| Spring Data | 2025.1 | 2026.0.1 |
| Spring Batch | 6.0 | 6.0.5 |
| Spring Kafka | 4.0 | 4.1.1 |
| Spring AMQP | 4.0 | 4.1.1 |
| Hibernate ORM | 7.2 | 7.4.5.Final |
| Hibernate Validator | 9.0 | 9.1 |
| HikariCP | 7.0 | 7.0.2 |
| Tomcat | 11.0 | 11.0.24 |
| Testcontainers | 2.0 | 2.0.5 |
| Flyway | 11.11 | 12.4.0 |
| Jackson | 3.0 | 3.1.5 |
| Micrometer | 1.16 | 1.17.1 |
写版本号时以「4.1.1 实测」列为准。 典型错误是写「4.x 用 Hibernate 7.2」——那是 4.0 的口径,4.1 已升到 7.4。凡是你项目里显式覆盖过版本的依赖(Spring Cloud、某些 driver),都要单独确认兼容版本。
2c. 被移除的依赖管理。 有两处需要特别注意:
- Spring Retry:依赖管理被移除(Spring 生态转向 Framework 7 的核心重试能力)。如果你的代码仍依赖 Spring Retry,必须自己写版本号,并考虑迁到 Spring Framework。
- Spring Authorization Server:并入 Spring Security 7.0。你不能再用
spring-authorization-server.version覆盖版本,要改用spring-security.version。
11.2.5 第 3 步:类与注解的移除与改名
依赖对了之后,编译器会精确报出「真正缺的类」。对照这张表逐项改:
| 3.x | 4.x | 说明 |
|---|---|---|
@MockBean | 已移除 → @MockitoBean | 测试用,见 11.2.7 |
@SpyBean | 已移除 → @MockitoSpyBean | 同上 |
Jackson2ObjectMapperBuilderCustomizer | JsonMapperBuilderCustomizer | 包 org.springframework.boot.jackson.autoconfigure |
@JsonComponent | @JacksonComponent | 包 org.springframework.boot.jackson |
@JsonMixin | @JacksonMixin | 同上 |
@EntityScan | org.springframework.boot.persistence.autoconfigure.EntityScan | 包迁移 |
BootstrapRegistry | org.springframework.boot.bootstrap.BootstrapRegistry | 包迁移 |
EnvironmentPostProcessor | org.springframework.boot.EnvironmentPostProcessor | 旧位置 org.springframework.boot.env 已废弃 |
org.springframework.lang.Nullable | org.jspecify.annotations.Nullable | JSpecify 空安全 |
这些名字都能从本机 jar 核实。测试注解的包名可以这样查(本机已下 7.0.9 的 spring-test):
# @MockitoBean 的真实包名
unzip -l /tmp/springboot_book/jars/spring-test-7.0.9.jar | rg 'MockitoBean\.class'
实测输出:org/springframework/test/context/bean/override/mockito/MockitoBean.class——即 org.springframework.test.context.bean.override.mockito.MockitoBean。注意 @MockitoBean 是 Spring Framework 的注解,不是 Spring Boot 的,所以它不在 spring-boot-* 的任何模块里。
Jackson 3 的类改名同样可核实:
unzip -l ~/.m2/repository/org/springframework/boot/spring-boot-jackson/4.1.1/spring-boot-jackson-4.1.1.jar \
| rg 'JacksonComponent|JacksonMixin|JsonMapperBuilderCustomizer'
11.2.6 第 4 步:配置属性重命名与移除
属性改名的原则是「先让工具帮你找,再逐条改」。官方提供了一个迁移器模块:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
它会分析应用的 Environment,在启动时打印诊断,并在运行时临时帮你迁移属性。迁移完成后必须删掉它,否则每次启动都在做无谓的属性映射。
下面这张表是从官方 4.0 配置变更日志里核实过的常见重命名,按影响面排序:
| 3.x | 4.x |
|---|---|
spring.http.client.* | spring.http.clients.* |
spring.http.reactiveclient.* | spring.http.clients.* / spring.http.clients.reactive.* |
spring.data.mongodb.host/port/uri/...(driver 相关) | spring.mongodb.* |
spring.session.redis.* | spring.session.data.redis.* |
spring.dao.exceptiontranslation.enabled | spring.persistence.exceptiontranslation.enabled |
management.health.mongo.enabled | management.health.mongodb.enabled |
management.health.probes.enabled | management.endpoint.health.probes.enabled |
management.endpoint.<id>.enabled | management.endpoint.<id>.access |
management.metrics.mongo.* | management.metrics.mongodb.* |
有几条要单独拎出来:
management.endpoint.<id>.enabled→.access是一整族变更,Actuator 的每个端点都受影响。别只改一个就以为改完了。- MongoDB 的属性拆分很微妙:只有「仅需 driver」的属性才从
spring.data.mongodb改成spring.mongodb;需要 Spring Data MongoDB 的(如spring.data.mongodb.auto-index-creation、spring.data.mongodb.repositories.type)保持不变。改错方向会让配置静默不生效。 - Jackson 属性:
spring.jackson.read.*/write.*(JSON 专属)在 4.0 挪到spring.jackson.json.read/.write;spring.jackson.parser.*被spring.jackson.json.read取代。4.1 又把跨格式通用的spring.jackson.read.*/write.*重新引入(作用于 CBOR/JSON/XML 共有特性)。所以同名属性在 4.0 和 4.1 含义不同,升级时务必分清。
11.2.7 第 5 步:spring.factories 到 AutoConfiguration.imports 的沿革
如果你自己写过 starter 或自动配置,这一条必须懂。注册自动配置的机制经历了两代:
| 版本 | 机制 | 状态 |
|---|---|---|
| 2.x | META-INF/spring.factories,键 org.springframework.boot.autoconfigure.EnableAutoConfiguration | 旧机制 |
| 2.7 | 引入 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,同时兼容 spring.factories | 过渡期 |
| 3.0 | 移除 spring.factories 里 EnableAutoConfiguration 键的支持 | 只认 imports 文件 |
| 4.x | 模块化后,每个模块各自持有自己的 AutoConfiguration.imports | 现行 |
也就是说,从 3.0 起 spring.factories 里写自动配置就已经不生效了;4.x 进一步把「谁登记自动配置」按模块拆开——每个技术模块只登记自己的。spring.factories 里其它键(如 org.springframework.boot.env.EnvironmentPostProcessor)仍有效,不受影响。
你可以直接看本机模块的登记文件,验证「各模块各管各的」:
# 数一数各模块登记了几条自动配置
for m in spring-boot-autoconfigure spring-boot-webmvc spring-boot-tomcat spring-boot-jackson; do
n=$(unzip -p ~/.m2/repository/org/springframework/boot/$m/4.1.1/$m-4.1.1.jar \
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2>/dev/null | rg -c .)
echo "$m: $n"
done
本机实测:spring-boot-autoconfigure: 12、spring-boot-webmvc: 6、spring-boot-tomcat: 5、spring-boot-jackson: 1。
还有一处历史遗留要注意:spring.factories 里注册 EnvironmentPostProcessor 的键在 4.0 仍然有效,但该接口的包名变了(见 11.2.5)。如果你的 spring.factories 里写了旧包名,它会静默失效——这类「注册了但不生效」的问题比编译错误难查得多。
11.2.8 第 6 步:测试写法适配
4.0 的测试层变化集中且容易漏,单列一张表:
| 3.x | 4.x |
|---|---|
@MockBean / @SpyBean | 已移除 → @MockitoBean / @MockitoSpyBean |
@SpringBootTest 自带 MockMvc | 需加 @AutoConfigureMockMvc |
@SpringBootTest 自带 TestRestTemplate | 需加 @AutoConfigureTestRestTemplate |
MockitoTestExecutionListener | 已移除,改用 Mockito 的 MockitoExtension |
@PropertyMapping | 移到 org.springframework.boot.test.context |
| — | 新增 RestTestClient + @AutoConfigureRestTestClient |
@MockitoBean 有一个关键差异:它可以用作测试类的字段,但不能用在 @Configuration 类里。3.x 里常见的「用一个 @TestConfiguration 集中声明一批 mock」的写法在 4.x 会失败。替代做法是把它们直接标在测试类上:
@SpringBootTest
@MockitoBean(types = {OrderService.class, UserService.class})
@MockitoBean(name = "ps1", types = PrintingService.class)
class ApplicationTests {
@Test
void check() {
// ...
}
}
如果这类共享 mock 很多、写在测试类上不现实,可以自定义一个组合注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = {OrderService.class, UserService.class})
@MockitoBean(name = "ps1", types = PrintingService.class)
public @interface SharedMocks {
}
测试模块的引入也要注意:@WithMockUser / @WithUserDetails 现在需要 spring-boot-starter-security-test 才能正常工作,只引 spring-boot-starter-test 不够。而所有 spring-boot-starter-<tech>-test 都会传递 spring-boot-starter-test,所以正确做法是「引被测技术对应的 test starter」,而不是继续显式引 spring-boot-starter-test。
11.2.9 属性元数据:最权威的核实手段
改配置属性时,不要靠记忆,也不要靠搜索引擎。每个模块 jar 里都有 META-INF/spring-configuration-metadata.json,它才是属性名最权威的来源:
# 列出 spring.jackson 命名空间下所有真实存在的属性
unzip -p ~/.m2/repository/org/springframework/boot/spring-boot-jackson/4.1.1/spring-boot-jackson-4.1.1.jar \
META-INF/spring-configuration-metadata.json | rg -o '"name": "spring\.jackson\.[^"]*"' | sort -u
本机实测能确认 spring.jackson.json.read、spring.jackson.json.write、spring.jackson.read、spring.jackson.write、spring.jackson.parser、spring.jackson.find-and-add-modules、spring.jackson.use-jackson2-defaults 都存在——这直接印证了 11.2.6 里「4.0 挪到 json 子命名空间、4.1 又重引入通用读写」的判断。当文档、记忆、博客三者的说法冲突时,以这个 json 为准。
小结
这一节的核心不是那张长长的改名表,而是步骤之间的依赖关系:清废弃 → 抬基线 → 定依赖 → 改代码 → 改配置 → 改注册 → 改测试,每一步都为下一步扫清干扰。
- 第 0 步在 3.5.x 上把废弃调用清零,4.x 的报错才会只剩模块化一条线索。
- 基线先抬:Java 17+/21、Jakarta EE 11、Servlet 6.1、Spring Framework 7.x;4.1 起
-DskipTests不再跳过 AOT,改用maven.test.skip。 - 依赖改名 + 补 Flyway / Liquibase starter;版本以 4.1.1 实测口径为准,别混 4.0 与 4.1。
- 类与注解改名集中在 Jackson 3、JSpecify、包迁移三块;
@MockitoBean属于 Spring Framework 而非 Boot。 - 配置属性用
spring-boot-properties-migrator辅助,改完必须移除;MongoDB 属性按「是否仅需 driver」拆分。 spring.factories注册自动配置从 3.0 起已失效,4.x 改为每模块各持AutoConfiguration.imports。- 属性名有疑问就
unzip读spring-configuration-metadata.json。
清单能保证「不漏项」,但保证不了「不出错」。下一节讲迁移过程中怎么分批验证、哪些改动不可回滚、什么情况下应当干脆放弃迁移。
阅读导航:上一节:11.1 Spring Boot 4 的模块化重构 · 下一节:11.3 迁移实测与回滚策略 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。