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%的启动时间缩短能直接降低运行成本;而空安全校验则是长期的价值,能持续减少生产环境的空指针故障。

相关推荐
2501_941982051 小时前
2026马年大吉:基于 Java 的企微外部群主动调用体系
java·开发语言·企业微信
独自破碎E1 小时前
题解 | 灵异背包?
android·java·开发语言
J_liaty1 小时前
Spring Boot 邮件发送完整指南:带附件、内嵌图片与中文乱码根治方案
java·spring boot·spring·email
sheji70091 小时前
Springboot家教平台中心系统53754--(程序+源码+数据库+调试部署+开发环境)
java·数据库·spring boot·后端·spring·旅游
AskHarries1 小时前
GitHub Login 对接配置指南
后端
VXbishe1 小时前
基于web的校园失物招领管理系统-计算机毕设 附源码 24150
javascript·vue.js·spring boot·python·node.js·php·html5
QQ 31316378902 小时前
文华支撑压力画线主图指标公式源码
java
星火开发设计2 小时前
类模板:实现通用数据结构的基础
java·开发语言·数据结构·c++·html·知识
小宋10212 小时前
Java 数据库访问 vs Python 数据库访问:JDBC vs ORM
java·数据库·python