Spring Boot 4 升级实战:从3.x到4.0的分步升级保姆级指南

Spring Boot 4.0于2025年11月20日正式发布,是继2.x到3.x之后框架的又一次重大重构。本次升级将单体自动配置拆分为47个轻量模块、原生集成JSpecify空安全校验、内置API版本控制能力,同时基于Spring Framework 7.0打造,带来了更优的性能和开发体验。Spring Boot 3.5.x的支持将持续至2026年11月,为开发者预留了充足的迁移时间,而新特性带来的性能提升和开发效率优化,让迁移具备极高的实际价值。

本文基于生产环境服务的迁移实践,从版本前置升级、环境检查、核心配置修改、空安全修复、API版本控制配置等方面,提供可落地的分步迁移指南,同时梳理迁移过程中的常见问题与解决方案,帮你平稳完成从Spring Boot 3.5到4.0的升级。

一、Spring Boot 4.0 核心变更与升级价值

Spring Boot 4.0的核心更新围绕模块化、空安全、原生功能增强展开,最低要求Java 17(推荐Java 21 LTS),Kotlin项目需升级至2.2及以上版本,核心变更及升级带来的实际价值如下:

1.1 核心变更

  1. 自动配置模块化 :将原6.2MB的spring-boot-autoconfigure单体JAR拆分为47个专属轻量模块,引入spring-boot-starter-web仅加载WebMVC配置,不再包含批处理、MongoDB等无关配置;
  2. 空安全体系升级 :使用JSpecify 1.0替代原org.springframework.lang的空注解,支持编译期空安全校验,提前规避NPE问题;
  3. 原生API版本控制 :无需自定义RequestMappingHandlerMapping或路径拼接,通过注解和配置即可实现Header/路径式API版本控制;
  4. 声明式HTTP客户端:内置声明式HTTP客户端,无需依赖Feign等第三方库;
  5. 可观测性增强:集成Micrometer 2.0,支持SSL健康检查,监控能力更完善;
  6. 依赖与测试调整 :移除MockitoTestExecutionListener,需改用MockitoExtension;精简spring-boot-starter-parent结构;核心消息抽象迁移至spring-messaging模块。

1.2 实际升级价值

基于生产服务的迁移实测,Spring Boot 4.0相比3.5.x带来了显著的性能和开发体验提升:

  • 镜像体积减少19%:从387MB降至312MB,降低容器部署的存储和网络成本;
  • 启动时间缩短33%:从4.2秒降至2.8秒,提升服务弹性扩缩容效率;
  • 消除冗余代码:原生API版本控制可移除200行左右的自定义路由代码;
  • 提前规避BUG:编译期空安全校验可发现潜在的空指针问题,减少生产环境故障;
  • 依赖更简洁:模块化的自动配置让依赖图谱更清晰,减少无用依赖的加载。

二、迁移前置准备

2.1 版本前置升级:先升级至3.5.x最新版

禁止直接从3.3及以下版本跳级至4.0,需先升级到Spring Boot 3.5.x的最新版本(截至2025年12月为3.5.6),该步骤可提前暴露弃用警告,确保依赖的兼容性。

Maven配置修改
xml 复制代码
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.5.6</version>
</parent>
Gradle配置修改
groovy 复制代码
plugins {
  id 'org.springframework.boot' version '3.5.6'
}

升级后执行测试,修复所有失败用例:

bash 复制代码
# Maven
./mvnw clean test
# Gradle
./gradlew clean test

关键修复 :3.4开始弃用MockitoTestExecutionListener,4.0直接移除,若测试类使用@Mock/@Captor但未引入MockitoExtension,会出现Mock对象为null的问题,需在测试类添加:

java 复制代码
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class MyServiceTest {
  @Mock
  private MyRepository repo;
  // 测试逻辑
}

2.2 检查并升级Java/Kotlin版本

Spring Boot 4.0要求Java 17及以上 (推荐Java 21 LTS),Kotlin项目需Kotlin 2.2及以上,先检查当前版本:

bash 复制代码
java -version
Java 21 安装(主流系统)
  • macOS(Homebrew):

    bash 复制代码
    brew install openjdk@21
  • Ubuntu:

    bash 复制代码
    sudo apt update
    sudo apt install openjdk-21-jdk
构建文件中指定Java版本
  • Maven(pom.xml):

    xml 复制代码
    <properties>
      <java.version>21</java.version>
    </properties>
  • Gradle(build.gradle):

    groovy 复制代码
    java {
      sourceCompatibility = JavaVersion.VERSION_21
      targetCompatibility = JavaVersion.VERSION_21
    }
Kotlin版本升级(pom.xml)
xml 复制代码
<kotlin.version>2.2.0</kotlin.version>

修改后重新构建并测试,确保基础环境无问题。

三、正式升级至Spring Boot 4.0.0

完成前置准备后,将Spring Boot版本正式修改为4.0.0,这一步是迁移的核心,会出现依赖和编译相关的错误,需逐一修复。

3.1 修改构建文件版本

Maven
xml 复制代码
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.0.0</version>
</parent>
Gradle
groovy 复制代码
plugins {
  id 'org.springframework.boot' version '4.0.0'
}

3.2 执行构建并修复依赖缺失问题

bash 复制代码
# Maven
./mvnw clean package
# Gradle
./gradlew clean build

最常见错误 :模块化拆分后,直接导入自动配置类但未引入对应starter的,会出现类缺失,需添加专属starter依赖。

示例:使用Spring Data MongoDB需添加:

xml 复制代码
<!-- 非响应式 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- 响应式 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

若未使用starter,需手动添加对应的自动配置模块(模块列表参考Spring Boot 4.0官方迁移指南),例如使用TestRestTemplate需添加:

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-autoconfigure-web</artifactId>
</dependency>

四、修复JSpecify空安全警告

Spring Boot 4.0全面采用JSpecify 1.0 的空注解(org.jspecify.annotations),替代原Spring的空注解(org.springframework.lang),支持编译期空安全校验,是本次升级的核心重点之一。

4.1 添加JSpecify依赖

xml 复制代码
<dependency>
  <groupId>org.jspecify</groupId>
  <artifactId>jspecify</artifactId>
  <version>1.0.0</version>
</dependency>

4.2 开启包级别的空安全标记

创建package-info.java文件,标记当前包为默认非空 ,仅显式标注@Nullable的对象可为空,实现全局空安全约束:

java 复制代码
@NullMarked
package com.example.myapp;

import org.jspecify.annotations.NullMarked;

4.3 修复具体的空安全警告

添加依赖和标记后,IDEA 2025.3+/Eclipse(Spring Tools)会在编译期提示空安全警告,核心修复场景为未处理可空对象的空值情况

典型场景:仓库查询结果未判空

原代码(有警告)

java 复制代码
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
  // findById返回@Nullable User/Optional<User>,直接返回会触发警告
  return userRepository.findById(id);
}

修复后代码

java 复制代码
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
  // 方式1:Optional判空
  return userRepository.findById(id)
    .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
  // 方式2:直接判空
  /*
  User user = userRepository.findById(id);
  if (user == null) {
    throw new ResponseStatusException(HttpStatus.NOT_FOUND);
  }
  return user;
  */
}

4.4 (可选)构建期强制空安全校验

若需要在CI/构建阶段强制校验空安全,可集成NullAway,拒绝空安全违规的代码构建(需Java 21+),Maven配置示例:

xml 复制代码
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <compilerArgs>
      <arg>-XDaddTypeAnnotationsToSymbol=true</arg>
      <arg>-Xplugin:NullAway</arg>
    </compilerArgs>
    <annotationProcessorPaths>
      <path>
        <groupId>com.uber.nullaway</groupId>
        <artifactId>nullaway</artifactId>
        <version>0.10.12</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>

执行mvn compile,若存在空安全违规,构建会直接失败。

五、配置原生API版本控制

Spring Boot 4.0内置API版本控制能力,无需自定义代码,支持Header式路径式两种方式,彻底替代传统的路径拼接/自定义处理器方案。

5.1 核心配置:选择版本控制方式

application.properties/application.yml中配置版本控制的核心规则,二选一即可。

方式1:Header式版本控制(推荐)

通过请求头传递API版本,保持URL整洁,适合内部服务/前后端分离项目:

properties 复制代码
# 自定义头名称为API-Version
spring.mvc.apiversion.use.header=API-Version
方式2:路径式版本控制

通过URL路径段传递API版本,适合浏览器端/无请求头控制的场景:

properties 复制代码
# 数字1表示版本为URL的第2个路径段(索引从0开始)
spring.mvc.apiversion.use.path-segment=1
# 示例:/api/v1.2/users → 版本为v1.2

5.2 接口中添加版本注解

@GetMapping/@PostMapping等注解中通过version属性指定接口版本,支持多版本接口共存。

Header式版本控制示例
java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
@RequestMapping("/api/users")
public class UserController {

  // 1.0版本接口:返回旧版VO
  @GetMapping(value = "/{id}", version = "1.0")
  public UserV1 getUserV1(@PathVariable Long id) {
    return userService.getV1User(id);
  }

  // 1.1版本接口:返回新版VO(含扩展字段)
  @GetMapping(value = "/{id}", version = "1.1")
  public UserV2 getUserV2(@PathVariable Long id) {
    return userService.getV2User(id);
  }
}
客户端调用示例(curl)
bash 复制代码
# 调用1.0版本
curl -H "API-Version: 1.0" http://localhost:8080/api/users/1
# 调用1.1版本
curl -H "API-Version: 1.1" http://localhost:8080/api/users/1

5.3 高级用法:基线版本匹配

使用1.0+表示匹配1.0及以上所有版本,避免未变更接口的版本注解重复编写:

java 复制代码
// 匹配1.0、1.1、1.2等版本,除非有更具体的版本接口
@GetMapping(value = "/list", version = "1.0+")
public List<UserV1> getUserList() {
  return userService.listV1Users();
}

5.4 服务间调用:API版本自动注入

使用RestClient调用其他服务时,可配置版本注入器,自动添加版本头,无需手动设置:

java 复制代码
import org.springframework.web.client.RestClient;
import org.springframework.web.servlet.mvc.method.annotation.ApiVersionInserter;

RestClient client = RestClient.builder()
  .baseUrl("http://localhost:8080")
  // 配置Header式版本注入
  .apiVersionInserter(ApiVersionInserter.useHeader("API-Version"))
  .build();

// 调用时指定版本,自动添加API-Version:1.1头
UserV2 user = client.get()
  .uri("/api/users/1")
  .apiVersion("1.1")
  .retrieve()
  .body(UserV2.class);

六、替换所有弃用的API

Spring Boot 4.0移除了大量弃用的类和注解,需在IDE中检查删除线标注的弃用代码,替换为官方推荐的替代方案,核心替换点如下:

6.1 空注解替换

java 复制代码
// 旧:Spring原生注解
import org.springframework.lang.Nullable;
// 新:JSpecify注解
import org.jspecify.annotations.Nullable;

6.2 Mock相关注解替换

java 复制代码
// 旧
import org.springframework.boot.test.mock.mockito.MockBean;
// 新(3.5+推荐)
import org.springframework.test.context.bean.override.mockito.MockitoBean;
// 或直接使用@Mock + MockitoExtension(推荐)

6.3 核心类包迁移

核心消息抽象从spring-context迁移至spring-messaging,若使用相关类,需调整导入包(IDE会自动提示)。

七、迁移过程中的常见问题与解决方案

结合生产服务的迁移实践,梳理6类最常见的问题及快速解决方案,覆盖依赖、测试、配置等核心场景。

问题1:TestRestTemplate 无法解析

错误Cannot resolve symbol 'TestRestTemplate'
解决方案 :添加spring-boot-starter-test依赖(测试环境)或手动添加web自动配置模块:

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

问题2:MongoTemplate/RedisTemplate 注入失败

错误No qualifying bean of type 'org.springframework.data.mongodb.core.MongoTemplate'
解决方案:模块化后,需添加对应的数据库starter,而非仅依赖核心包:

xml 复制代码
<!-- MongoDB -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Redis -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

问题3:@Mock 字段为null,测试报NPE

错误NullPointerException(Mock对象未初始化)
解决方案 :测试类添加@ExtendWith(MockitoExtension.class),移除弃用的@RunWith(MockitoJUnitRunner.class)

问题4:JSpecify 注解无提示,IDEA不识别

解决方案

  1. 升级IDEA至2025.3及以上版本;
  2. 项目SDK设置为Java 21;
  3. 确保添加了JSpecify 1.0.0依赖;
  4. IDEA中开启File > Settings > Build, Execution, Deployment > Compiler > Java Compiler的注解处理。

问题5:路径式版本控制返回404

错误 :配置路径式版本控制后,所有请求返回404
解决方案@RequestMapping中必须包含{version}路径变量,Spring通过该变量提取版本:

java 复制代码
// 正确
@RequestMapping("/api/{version}/users")
// 错误
@RequestMapping("/api/users")

问题6:Maven依赖冲突,Spring Framework版本低于7.0

错误Dependency convergence error(依赖树中存在Spring Framework <7.0的版本)
解决方案:查看依赖树,找到引入低版本Spring的依赖并升级/排除:

bash 复制代码
# 查看Maven依赖树
./mvnw dependency:tree
# 排除低版本依赖示例
<dependency>
  <groupId>com.example</groupId>
  <artifactId>old-dependency</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </exclusion>
  </exclusions>
</dependency>

八、Spring Boot 3.5 vs 4.0 核心指标对比

基于实际生产服务的迁移实测,核心指标对比如下,直观体现升级的价值:

指标 Spring Boot 3.5.6 Spring Boot 4.0.0 变化
容器镜像体积 387 MB 312 MB 减少19%
服务启动时间 4.2s 2.8s 缩短33%
空安全校验 编译期警告/强制 提前规避NPE
API版本控制 需自定义代码 框架原生支持 移除200行代码
自动配置结构 1个单体JAR 47个轻量模块 依赖更简洁
Java基线 17(可选21) 17(推荐21) 一致
Kotlin基线 1.9 2.2 强制升级
测试框架 支持旧版Mockito 仅支持MockitoExtension 规范测试写法

九、后续规划与生态展望

9.1 版本支持与后续升级

  • Spring Boot 3.5.x:官方支持至2026年11月,可根据业务节奏择机迁移;
  • Spring Boot 4.0.1:2025年12月9日发布,仅修复小BUG,无破坏性变更,可直接升级;
  • Spring Boot 4.1:预计2026年Q2发布,重点增强响应式能力、进一步模块化,同时优化GraalVM原生镜像构建,降低无服务应用的冷启动时间。

9.2 生态兼容注意事项

目前部分第三方库尚未完成Spring Framework 7.0的适配,迁移前需检查核心依赖的兼容性:

  1. 优先使用Spring官方生态的依赖,兼容性最高;
  2. 自定义starter/内部库需提前完成4.0适配;
  3. 日志、监控等通用库,优先升级至最新版本。

9.3 迁移后的最佳实践

  1. 全面推行空安全编码:基于JSpecify规范,所有新代码添加空注解,逐步改造旧代码;
  2. 基于原生API版本控制:统一团队的API版本规范,避免自定义方案的碎片化;
  3. 精简依赖:移除无用的starter,利用模块化优势进一步降低镜像体积;
  4. 开启构建期空安全强制校验:在CI/CD流水线中集成NullAway,拒绝空安全违规代码合并。

十、总结

Spring Boot 4.0是一次高性能、高安全性、高开发效率的重大升级,模块化的自动配置、原生的空安全校验、内置的API版本控制三大核心特性,不仅带来了显著的性能提升,更从框架层面规范了开发流程,提前规避生产环境的常见BUG。

本次迁移的核心原则是分步升级、前置修复:先升级至3.5.x最新版,修复弃用警告,再检查并升级基础环境,最后正式升级至4.0并修复依赖、编译问题。从实际实践来看,无复杂自定义配置的服务,90分钟内可完成迁移;存在自定义自动配置/多依赖的服务,约4小时可完成,整体迁移成本可控。

对于拥有公共API的服务,原生API版本控制 特性足以成为迁移的核心理由;对于追求性能的容器/无服务部署场景,19%的镜像体积减少+33%的启动时间缩短能直接降低运行成本;而空安全校验则是长期的价值,能持续减少生产环境的空指针故障。

相关推荐
追逐时光者4 分钟前
一个基于 .NET Core + Vue3 构建的开源全栈平台 Admin 系统
后端·.net
程序员飞哥10 分钟前
90后大龄程序员失业4个月终于上岸了
后端·面试·程序员
zs宝来了44 分钟前
Playwright 自动发布 CSDN 的完整实践
java
彭于晏Yan2 小时前
Redisson分布式锁
spring boot·redis·分布式
吴声子夜歌2 小时前
TypeScript——基础类型(三)
java·linux·typescript
GetcharZp2 小时前
Git 命令行太痛苦?这款 75k Star 的神级工具,让你告别“合并冲突”恐惧症!
后端
Victor3563 小时前
MongoDB(69)如何进行增量备份?
后端
Victor3563 小时前
MongoDB(70)如何使用副本集进行备份?
后端
DynamicsAgg3 小时前
企业数字化底座-k8s企业实践系列第二篇pod创建调度
java·容器·kubernetes