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 核心变更
- 自动配置模块化 :将原6.2MB的
spring-boot-autoconfigure单体JAR拆分为47个专属轻量模块,引入spring-boot-starter-web仅加载WebMVC配置,不再包含批处理、MongoDB等无关配置; - 空安全体系升级 :使用JSpecify 1.0替代原
org.springframework.lang的空注解,支持编译期空安全校验,提前规避NPE问题; - 原生API版本控制 :无需自定义
RequestMappingHandlerMapping或路径拼接,通过注解和配置即可实现Header/路径式API版本控制; - 声明式HTTP客户端:内置声明式HTTP客户端,无需依赖Feign等第三方库;
- 可观测性增强:集成Micrometer 2.0,支持SSL健康检查,监控能力更完善;
- 依赖与测试调整 :移除
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):
bashbrew install openjdk@21 -
Ubuntu:
bashsudo apt update sudo apt install openjdk-21-jdk
构建文件中指定Java版本
-
Maven(pom.xml):
xml<properties> <java.version>21</java.version> </properties> -
Gradle(build.gradle):
groovyjava { 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不识别
解决方案:
- 升级IDEA至2025.3及以上版本;
- 项目SDK设置为Java 21;
- 确保添加了JSpecify 1.0.0依赖;
- 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的适配,迁移前需检查核心依赖的兼容性:
- 优先使用Spring官方生态的依赖,兼容性最高;
- 自定义starter/内部库需提前完成4.0适配;
- 日志、监控等通用库,优先升级至最新版本。
9.3 迁移后的最佳实践
- 全面推行空安全编码:基于JSpecify规范,所有新代码添加空注解,逐步改造旧代码;
- 基于原生API版本控制:统一团队的API版本规范,避免自定义方案的碎片化;
- 精简依赖:移除无用的starter,利用模块化优势进一步降低镜像体积;
- 开启构建期空安全强制校验:在CI/CD流水线中集成NullAway,拒绝空安全违规代码合并。
十、总结
Spring Boot 4.0是一次高性能、高安全性、高开发效率的重大升级,模块化的自动配置、原生的空安全校验、内置的API版本控制三大核心特性,不仅带来了显著的性能提升,更从框架层面规范了开发流程,提前规避生产环境的常见BUG。
本次迁移的核心原则是分步升级、前置修复:先升级至3.5.x最新版,修复弃用警告,再检查并升级基础环境,最后正式升级至4.0并修复依赖、编译问题。从实际实践来看,无复杂自定义配置的服务,90分钟内可完成迁移;存在自定义自动配置/多依赖的服务,约4小时可完成,整体迁移成本可控。
对于拥有公共API的服务,原生API版本控制 特性足以成为迁移的核心理由;对于追求性能的容器/无服务部署场景,19%的镜像体积减少+33%的启动时间缩短能直接降低运行成本;而空安全校验则是长期的价值,能持续减少生产环境的空指针故障。