万字避坑:Spring Boot 3.x 升 4.0 最全升级指南 - 附 Migration Checklist 建议收藏

作者:袖梨 2026-07-17

前言:升级不可怕,可怕的是不知道要改什么

兄弟们,升级 Spring Boot 大版本,最让人焦虑的是什么?

不是"写新代码",而是**"改旧代码"**——你到底要改哪些地方?改了会不会引发连锁故障?线上跑着的服务会不会突然挂掉?

好消息是,Spring Boot 4 的升级路径比 Boot 2→3 平滑得多。没有再来一次包名级别的全量地震(那个已经在 Boot 3 做完了),但仍有一批"必须改"和"建议改"的要点。

本文逐一拆解 10 大类升级变更项,每项都基于 Spring Boot 4.1.0 源码验证,并标注影响等级。先看这张等级表,心里有数:

影响等级含义你需要做什么
必须改不改编译/启动失败立刻改,没商量
建议改旧方式仍可用但已被弃用,未来版本将移除有空就改,别拖
零成本收益新增能力,不需要改代码就能享受白嫖,爽

️ 先认识一个"救命"模块:spring-boot-properties-migrator

在逐项讲解前,先给大家介绍一个升级路上的"救命稻草"——spring-boot-properties-migrator。这个模块能帮你自动检测和迁移已弃用的配置项,省去大量人工排查的功夫。

源码切入点

源码位置:core/spring-boot-properties-migrator/

这个模块的核心类是 PropertiesMigrationListener,它实现了 ApplicationListener<SpringApplicationEvent>,在应用启动的两个关键时刻介入:

 复制代码// PropertiesMigrationListener.java 关键逻辑
@Override
public void onApplicationEvent(SpringApplicationEvent event) {
    if (event instanceof ApplicationPreparedEvent preparedEvent) {
        onApplicationPreparedEvent(preparedEvent);  // ① 环境准备后扫描配置
    }
    if (event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) {
        logLegacyPropertiesReport();                 // ② 启动完成/失败时输出报告
    }
}

它的工作流程如下:

怎么用?一行依赖搞定

 复制代码<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-properties-migrator</artifactId>
    <scope>runtime</scope>
</dependency>
 复制代码// Gradle
runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")

启动应用后,如果配置文件中有已弃用的属性,控制台会直接告诉你该改成什么:

 复制代码WARN  [PropertiesMigrationListener] -
The use of configuration keys that have been renamed was found in the environment:Property source 'applicationConfig: [classpath:/application.yml]':
    Key: server.servlet.path
        Line: 3
        Replacement: spring.mvc.servlet.path

10 大类升级变更逐项指南(硬核预警)

接下来进入正文,每一项都附带源码证据 + 改写示例,大家可以对照自己的项目逐项排查。


变更 1:JDK 版本基线 — JDK 17 硬性要求,JDK 25 强烈推荐

源码证据:starter/spring-boot-starter-parent/build.gradle

 复制代码delegate."java.version"('17')

Spring Boot 4 的 JDK 17 是硬性编译基线,不可降级。同时,官方强烈推荐使用 JDK 25,以充分利用:

JDK 特性最低版本在 Spring Boot 4 中的应用
虚拟线程 GAJDK 21spring.threads.virtual.enabled=true 的前置条件
Record 类JDK 16 (GA 17)配置属性绑定、DTO 简化
Sealed ClassJDK 17模块化自动配置的类型层级
Pattern Matching for switchJDK 21框架内部条件判断简化
Scoped ValuesJDK 21 (GA 25)替代 ThreadLocal 的上下文传递(4.1 增强)
Stream GatherersJDK 24框架内部流处理优化

源码证据:Threading.java 中的 JDK 版本判断

 复制代码VIRTUAL {
    @Override
    public boolean isActive(Environment environment) {
        return environment.getProperty("spring.threads.virtual.enabled",
                boolean.class, false)
            && JavaVersion.getJavaVersion()
                .isEqualOrNewerThan(JavaVersion.TWENTY_ONE);  // 虚拟线程需要 JDK 21+
    }
};

升级操作:

 复制代码# 检查当前 JDK 版本
java -version# 推荐:安装 JDK 25(以 SDKMAN 为例)
sdk install java 25.0.1-tem
sdk use java 25.0.1-tem

变更 2:jakarta.* 包名 — 继续深化,Servlet 6.1 全面就绪

Spring Boot 4 底层使用 Tomcat 11.0.22(源码 gradle.properties 第 27 行):

 复制代码tomcatVersion=11.0.22

Tomcat 11 实现了 Jakarta Servlet 6.1 规范,对 javax.servlet.* 零容忍——任何残留都会导致 ClassNotFoundException。

需要确认的包名迁移清单:

旧包名 (javax)新包名 (jakarta)涉及场景
javax.servlet.*jakarta.servlet.*Filter、Servlet、Listener
javax.persistence.*jakarta.persistence.*JPA Entity、Repository
javax.validation.*jakarta.validation.*@Valid、@NotNull 等校验注解
javax.transaction.*jakarta.transaction.*JTA 事务管理(UserTransaction、TransactionManager)
javax.annotation.*jakarta.annotation.*@PostConstruct、@PreDestroy、@Resource
javax.mail.*jakarta.mail.*邮件发送
javax.json.*jakarta.json.*JSON 处理

一行命令快速排查(在项目根目录执行):

 复制代码# 检查是否还有 javax 包引用
grep -r "import javax." --include="*.java" src/ | grep -v "javax.annotation.processing"# 检查 Maven/Gradle 依赖中是否还有 javax 坐标
mvn dependency:tree | grep "javax."      # Maven
gradle dependencies | grep "javax."       # Gradle

如果仍有 javax 依赖的第三方库,可以使用 jakarta-transformer 工具进行字节码级别的自动转换:

 复制代码<!-- Maven plugin: 自动转换 javax → jakarta -->
<plugin>
    <groupId>org.eclipse.transformer</groupId>
    <artifactId>transformer-maven-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <execution>
            <goals><goal>transform</goal></goals>
        </execution>
    </executions>
</plugin>

变更 3:Jackson 2.x → 3.x — 核心类库包名全变,但注解原地不动

这是 Boot 4 升级中影响面最大的代码变更。Spring Boot 4 默认使用 Jackson 3(tools.jackson.* 包名),Jackson 2 仅作为过渡支持。

源码证据:gradle.properties

 复制代码jackson2Version=2.21.4    # 遗留支持(已弃用)
jacksonVersion=3.1.4      # 主力版本

核心变化:类库包名变了,但注解包名不变

 复制代码//  Jackson 2 核心类(Spring Boot 3.x 时代)
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;//  Jackson 3 核心类(Spring Boot 4.x 默认)
import tools.jackson.core.JsonFactory;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;//  注解包名保持不变!(向下兼容)
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;

整个 spring-boot-jackson2 模块已弃用

源码证据:module/spring-boot-jackson2/ 中所有核心类

 复制代码// JsonComponent.java 第 61-68 行
@Deprecated(since = "4.0.0", forRemoval = true)
public @interface JsonComponent { ... }// JsonObjectSerializer.java 第 35-38 行
@Deprecated(since = "4.0.0", forRemoval = true)
public abstract class JsonObjectSerializer<T> { ... }// Jackson2BackgroundPreinitializer.java 第 27-29 行
@Deprecated(since = "4.0.0", forRemoval = true)
public class Jackson2BackgroundPreinitializer { ... }

弃用时间线:Jackson 2 支持将在 Spring Boot 4.3.0 中彻底移除。也就是说,你有 4.0 → 4.3 的过渡窗口期,但别拖到最后。

必须修改的代码

 复制代码//  Boot 3.x:使用 Jackson2ObjectMapperBuilder
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
    return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL);
}//  Boot 4.x:使用 JsonMapper.builder() + JsonMapperBuilderCustomizer
@Bean
public JsonMapperBuilderCustomizer customizer() {
    return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL);
}

Spring Boot 4.1 新增的 Jackson 配置(spring.jackson.factory.*)

这个新配置可以帮你防御 JSON 炸弹攻击,之前需要手动写代码拦截的,现在配置文件搞定:

 复制代码spring:
  jackson:
    factory:
      constraints:
        read:
          max-nesting-depth: 500         # JSON 最大嵌套深度
          max-document-length: -1        # 最大文档长度(-1 不限)
          max-string-length: 100000000   # 最大字符串长度
          max-number-length: 1000        # 最大数字长度
          max-name-length: 50000         # 最大属性名长度
        write:
          max-nesting-depth: 500         # 序列化最大嵌套深度

批量替换脚本(懒人福音)

 复制代码# 查找所有使用 Jackson 2 核心包名的 Java 文件
grep -r "import com.fasterxml.jackson.(core|databind|dataformat)" --include="*.java" src/# 批量替换(macOS 使用 sed -i '')
find src/ -name "*.java" -exec sed -i '' 
  -e 's/import com.fasterxml.jackson.core./import tools.jackson.core./g' 
  -e 's/import com.fasterxml.jackson.databind./import tools.jackson.databind./g' 
  -e 's/import com.fasterxml.jackson.dataformat./import tools.jackson.dataformat./g' {} +# ️ 注意:不要替换注解包名!
# com.fasterxml.jackson.annotation.* 保持不变

变更 4:Undertow 被彻底移除 — 必须切换 Web 容器

在 Spring Boot 4.1.0 的源码中,没有任何 Undertow 相关文件:

 复制代码$ find . -name "*undertow*" 2>/dev/null
# 无结果 —— Undertow 已被完全移除

在 starter/ 目录下,只有以下 Web 容器 Starter:

 复制代码spring-boot-starter-tomcat        ← 默认(Servlet 容器)
spring-boot-starter-jetty         ← 备选(Servlet 容器)
spring-boot-starter-reactor-netty ← WebFlux 默认(响应式容器)

如果你之前在 Boot 3.x 中使用 Undertow

Maven 切换示例:

 复制代码<!--  Boot 3.x:使用 Undertow -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency><!--  Boot 4.x:切换为 Tomcat(默认,如果用了 spring-boot-starter-web 则无需额外添加) -->
<!-- Tomcat 已包含在 spring-boot-starter-web 的传递依赖中 --><!-- 或切换为 Jetty -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Gradle 切换示例:

 复制代码//  Boot 3.x
implementation("org.springframework.boot:spring-boot-starter-undertow")//  Boot 4.x — 排除默认 Tomcat,使用 Jetty
implementation("org.springframework.boot:spring-boot-starter-web") {
    exclude(module = "spring-boot-starter-tomcat")
}
implementation("org.springframework.boot:spring-boot-starter-jetty")

Undertow 特有配置需要迁移:

Undertow 配置 (已失效)对应 Tomcat/Jetty 配置
server.undertow.*迁移到 server.tomcat.* 或 server.jetty.*
server.undertow.accesslog.*server.tomcat.accesslog.*
Undertow 自定义 UndertowBuilderCustomizerTomcatServletWebServerFactoryCustomizer 或 JettyServletWebServerFactoryCustomizer

变更 5:AntPathMatcher 弃用 — 统一使用 PathPatternParser

源码证据:WebMvcProperties.java

 复制代码public enum MatchingStrategy {
    /**
     * @deprecated since 4.0.0 for removal in 4.2.0 in favor of
     * {@link #PATH_PATTERN_PARSER}
     */
    @Deprecated(since = "4.0.0", forRemoval = true)
    ANT_PATH_MATCHER,    PATH_PATTERN_PARSER
}

Spring Boot 4 统一使用 PathPatternParser 作为 URL 路径匹配策略,AntPathMatcher 已被弃用,将在 4.2.0 中移除。

受影响的配置:

 复制代码#  Boot 3.x:显式指定 AntPathMatcher
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher#  Boot 4.x:必须切换(或删除,默认就是 PATH_PATTERN_PARSER)
spring:
  mvc:
    pathmatch:
      matching-strategy: path_pattern_parser

如果你的代码中有自定义 AntPathMatcher:

 复制代码//  Boot 3.x
@Bean
public AntPathMatcher antPathMatcher() {
    return new AntPathMatcher();
}//  Boot 4.x:改用 PathPatternParser
@Bean
public PathPatternParser pathPatternParser() {
    PathPatternParser parser = new PathPatternParser();
    parser.setCaseSensitive(false);
    return parser;
}

变更 6:EnvironmentPostProcessor 包名迁移

源码证据:core/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessor.java

 复制代码/**
 * @deprecated since 4.0.0 for removal in 4.2.0 in favor of
 * {@link org.springframework.boot.EnvironmentPostProcessor}
 */
@FunctionalInterface
@Deprecated(since = "4.0.0", forRemoval = true)
public interface EnvironmentPostProcessor {
    // 旧接口,位于 org.springframework.boot.env 包
}

Spring Boot 4 将 EnvironmentPostProcessor 从 org.springframework.boot.env 包提升到 org.springframework.boot 包:

 复制代码//  Boot 3.x
import org.springframework.boot.env.EnvironmentPostProcessor;//  Boot 4.x
import org.springframework.boot.EnvironmentPostProcessor;

新的 EnvironmentPostProcessor 接口功能与旧接口基本一致,核心差异在于包名和构造器参数支持:

  • 包名从 org.springframework.boot.env 提升到 org.springframework.boot
  • 构造器可以接收 DeferredLogFactory(延迟日志工厂)和 ConfigurableBootstrapContext(可配置的启动上下文)
  • 排序方式:实现 Ordered 接口或标注 @Order 注解(与旧接口相同)

变更 7:RestTemplateBuilder.rootUri() 重命名为 baseUri()

源码证据:RestTemplateBuilder.java

 复制代码/**
 * @deprecated since 4.1.0 for removal in 4.3.0 in favor of {@link #baseUri(String)}
 */
@Deprecated(forRemoval = true, since = "4.1.0")
public RestTemplateBuilder rootUri(@Nullable String rootUri) {
    // ...
}
 复制代码//  Boot 4.0.x(4.1.0 中弃用)
RestTemplate restTemplate = new RestTemplateBuilder()
    .rootUri("http://api.example.com")
    .build();//  Boot 4.1.0+
RestTemplate restTemplate = new RestTemplateBuilder()
    .baseUri("http://api.example.com")
    .build();

变更 8:SessionProperties.determineTimeout() 弃用

源码证据:SessionProperties.java

 复制代码/**
 * @deprecated since 4.0.1 for removal in 4.2.0 in favor of {@link SessionTimeout}
 */
@Deprecated(since = "4.0.1", forRemoval = true)
public Duration determineTimeout(Supplier<Duration> fallbackTimeout) {
    return (this.timeout != null) ? this.timeout : fallbackTimeout.get();
}

如果代码中直接调用了 sessionProperties.determineTimeout(...),需要改为使用 SessionTimeout 机制。


变更 9:Health 模块三大类弃用

源码证据:module/spring-boot-health/src/main/java/

 复制代码// 以下三个类均 @Deprecated(since = "4.1.0", forRemoval = true)
@Deprecated(since = "4.1.0", forRemoval = true)
public class SimpleStatusAggregator { ... }@Deprecated(since = "4.1.0", forRemoval = true)
public interface HttpCodeStatusMapper { ... }@Deprecated(since = "4.1.0", forRemoval = true)
public class SimpleHttpCodeStatusMapper { ... }

如果你自定义了健康检查的状态聚合逻辑,需要使用新的 API 替代(Spring Boot 4.1 推荐使用 StatusAggregator 实现)。


变更 10:spring.factories 中 AutoConfiguration 键的彻底移除

Spring Boot 3.x 开始,自动配置类的注册方式从 spring.factories 中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 键迁移到 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。

Spring Boot 4 彻底完成了这个迁移。虽然 spring.factories 本身仍然用于其他 SPI(ApplicationContextInitializer、ApplicationListener、FailureAnalyzer 等),但自动配置类已不再从 spring.factories 读取。

源码证据:AutoConfigurationImportSelector.java 只读取 AutoConfiguration.imports

 复制代码// 自动配置只从 .imports 文件加载
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
        @Nullable AnnotationAttributes attributes) {
    ImportCandidates importCandidates = ImportCandidates.load(
            this.autoConfigurationAnnotation, getBeanClassLoader());
    List<String> configurations = importCandidates.getCandidates();
    Assert.state(!CollectionUtils.isEmpty(configurations),
            "No auto configuration classes found in META-INF/spring/"
                    + this.autoConfigurationAnnotation.getName() + ".imports.");
    return configurations;
}

如果你的项目有自定义 AutoConfiguration,需要确认注册方式:

 复制代码#  Boot 2.x 方式:META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.example.MyAutoConfiguration#  Boot 3.x/4.x 方式:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.MyAutoConfiguration

影响面分级总结:一张图看懂你要改什么

img_6a597b066022030.webp


升级决策树:跟着走,不迷路

不知道自己项目要改哪些?跟着这棵决策树走一遍:

img_6a597b066022531.webp

升级步骤 Checklist(建议收藏,对照打勾)

阶段一:环境准备

  • JDK 升级到 17+(推荐 JDK 25)
     复制代码java -version  # 确认版本
    
  • 更新 Spring Boot 版本号
     复制代码<!-- Maven parent -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.1.0</version>
    </parent>
    
  • 更新 Gradle 插件
     复制代码plugins {
        id 'org.springframework.boot' version '4.1.0'
    }
    

阶段二:依赖检查

  • 移除 Undertow 依赖,切换为 Tomcat 或 Jetty
  • 检查 Jackson 依赖
    • 如果直接依赖了 jackson-databind 等核心库,将 com.fasterxml.jackson.core/databind/dataformat 替换为 tools.jackson.*
    • 如果使用了 spring-boot-starter-json(通过 spring-boot-starter-web 间接引入的情况最常见):无需改动,该 Starter 在 Boot 4 中已自动切换为依赖 spring-boot-jackson(Jackson 3)
    • 注解依赖 (com.fasterxml.jackson.annotation.*) 不需要改
  • 检查是否有 javax.* 依赖残留
     复制代码mvn dependency:tree | grep "javax."  # Maven
    gradle dependencies | grep "javax."   # Gradle
    
  • 引入 spring-boot-properties-migrator(runtime scope,用于首次升级检测)

阶段三:代码修改

  • 替换 Jackson 核心类导入(com.fasterxml.jackson.core/databind/dataformat.* → tools.jackson.*)
  • 替换 Jackson2ObjectMapperBuilder → JsonMapper.builder()
     复制代码// 旧
    Jackson2ObjectMapperBuilder.json().build()
    // 新
    JsonMapper.builder().build()
    
  • 检查所有 Jackson2ObjectMapperBuilderCustomizer → JsonMapperBuilderCustomizer
  • 将 Jackson 2 注解/基类迁移到 Jackson 3 对应物(类名有变化):
    • @JsonComponent → @JacksonComponent
    • JsonObjectSerializer<T> → ObjectValueSerializer<T>
    • JsonObjectDeserializer<T> → ObjectValueDeserializer<T>
    • @JsonMixin → @JacksonMixin
    • JsonComponentModule → JacksonComponentModule
    • 模块坐标从 spring-boot-jackson2 → spring-boot-jackson
  • 替换 EnvironmentPostProcessor 导入
     复制代码// 旧
    import org.springframework.boot.env.EnvironmentPostProcessor;
    // 新
    import org.springframework.boot.EnvironmentPostProcessor;
    
  • 检查 RestTemplateBuilder 中的 .rootUri() → .baseUri()
  • 检查 AntPathMatcher 使用 → 替换为 PathPatternParser
  • 如果使用 SpringFox → 迁移到 SpringDoc OpenAPI
  • 检查自定义 SessionProperties.determineTimeout() 调用
  • 检查自定义 Health 模块中是否使用了 SimpleStatusAggregator / HttpCodeStatusMapper / SimpleHttpCodeStatusMapper

阶段四:配置修改

  • 移除 spring.mvc.pathmatch.matching-strategy=ant_path_matcher
  • 移除所有 server.undertow.* 配置
  • 确认 spring.factories 中的自动配置已迁移到 AutoConfiguration.imports

阶段五:启动验证

  • 首次启动,观察 PropertiesMigrationListener 输出的迁移报告
  • 修复所有 WARN/ERROR 级别的配置迁移提示
  • 全量运行单元测试和集成测试
  • 验证 Jackson 序列化/反序列化行为一致
  • 移除 spring-boot-properties-migrator 依赖(迁移完成后)

Spring Cloud 兼容矩阵

升级 Boot 4 的同学,Spring Cloud 也得跟着升。别搞错版本对应关系:

Spring Cloud 版本Spring Boot 版本Spring Framework状态
2025.0.x4.0.x / 4.1.x7.0.x 兼容
2024.0.x3.4.x6.2.x 不兼容 Boot 4
2023.0.x (Spring Cloud AWS 等)3.2.x / 3.3.x6.1.x 不兼容 Boot 4

总结:三句话讲清楚 Boot 3→4 升级

Spring Boot 3→4 的升级相比 2→3 友好得多。核心影响面集中在三个领域:

  1. Jackson 3 包名变更:核心类从 com.fasterxml.jackson → tools.jackson,这是最大的代码改动量
  2. Undertow 被移除:必须切换 Web 容器,但对于大多数已使用 Tomcat 的项目零影响
  3. 一批 API 的弃用与重命名:spring-boot-properties-migrator 能帮你自动发现和处理

建议的升级策略:先引入 migrator → 修正所有编译错误 → 启动验证 → 运行测试 → 移除 migrator,五步完成升级。


写在最后

如果这篇文章帮你少踩了一个坑,点个赞再走呗

有什么升级过程中遇到的问题,欢迎在评论区交流,我会一一回复。


相关文章

精彩推荐