Skip to content

哈喽,大家好,我是呼噜噜,近期Spring Boot 4.0.0已于 2025年11月20日 正式发布。

这是一个重大的主版本更新,标志着 Spring Boot 进入了新一代。它基于 Spring Framework 7.0 构建,主要聚焦于对最新 Java 特性的支持(如 Java 25)、Jakarta EE 11 规范的对齐以及云原生能力的增强。

不禁感叹Spring Boot3都还没学完,Spring Boot 4就都来了

那我们来看看这次Spring Boot 4.0.0 大版本更新的核心信息有哪些:

核心基准

  1. Java 版本: 最低要求 Java 17,但官方强烈推荐使用 Java 25(LTS)以利用虚拟线程等最新特性
  2. Spring Framework: 基于 Spring Framework 7.0
  3. Jakarta EE: 全面升级到 Jakarta EE 11(Servlet 6.1, JPA 3.2, Bean Validation 3.1)
  4. Kotlin: 基准提升至 Kotlin 2.2

关键新特性

我们再来看看这次大版本更新,还有有哪些新特性

全面拥抱 JSpecify 空安全

空指针NullPointerException可以说是我们javaer经常遇到的痛苦了,生产环境一遇到,头皮发麻。

它就像隐藏在代码深处的定时炸弹,往往在程序运行时突然爆发,导致程序崩溃,而且还非常难以排查

Spring Boot 4.0 全面拥抱 JSpecify 空安全体系,它的核心就是,默认非空(@NullMarked)+ 明确标注可空(@Nullable),能够在编译阶段就发现潜在的 Null 指针风险,提前预警

在类或包上加一个 @NullMarked,里面所有引用类型默认都是非空的,只有明确标了 @Nullable 的才可能为 null。这样:

  • 方法签名一目了然:返回值、参数到底会不会 null
  • 配合 NullAway 后,漏检查的地方直接编译失败;
  • 零运行时开销,完全兼容现有代码;
  • Optional 更轻量、更适合做大规模重构。
java
@org.jspecify.annotations.NullMarked  // 本 package 下默认所有类型为非 null
package com.example.demo.service;       // 根据你自己包名写

// 注意:NullMarked 也可放在 class 上,
// 或者对整个 module/packageTree 生效。

@NonNull:与 @Nullable 相反,@NonNull 注解用于强调某个元素绝对不能为 null:

java
@RestController
public class GreetingController {

    private final GreetingService greetingService;

    public GreetingController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }

    @GetMapping("/greet")
    public @NonNull Map<String, @Nullable String> greet(
        @RequestParam(required = false) @Nullable String name) { // name 可能为 null(用户没传),因此用 @Nullable

        String greeting = greetingService.greet(name);
        // greeting 按定义为非 null
        return Map.of("greeting", greeting);
    }
}

API 版本控制

4.0版本中,API 版本控制变得更加原生和标准化,引入了在 @RequestMapping / @GetMapping 等注解中使用 version 属性的能力,并提供 ApiVersionConfigurer 等工具来解析版本(支持 path/header/query/media-type

  1. 控制器示例(v1 / v2)
java
@RestController
@RequestMapping("/api/user")
public class UserController {
    @GetMapping(path = "/", version = "1")      //匹配 /api/user?version=1 或配置路径映射
    public Map<String, Object> getUsersV1() {
        return Map.of("version", "v1", "data", "user-list-v1");  // 返回示例
    }

    @GetMapping(path = "/", version = "2")                       // 版本 2 的映射
    public Map<String, Object> getUsersV2() {
        return Map.of("version", "v2", "data", "user-list-v2");  // 返回示例
    }
}
  1. application.properties配置示例
java

@Configuration                                                                                         // 标注为配置类
public class ApiVersionConfig implements WebMvcConfigurer {

    @Override
    public void configureApiVersioning(ApiVersionConfigurer configurer) {                             // 配置 API 版本化策略
        configurer.useHeader("API-Version");                                                           // 指定从 header 名称 API-Version 中读取版本
        configurer.setVersionRequired(false);                                                          // 当没有提供版本时,允许兼容所有版本(false)或强制要求版本(true)
    }
}

只需修改application.properties,就能切换策略,就能轻松实现URI前缀、Header或参数驱动的版本路由

plain
spring.mvc.apiversion.use.header=API-Version
spring.mvc.apiversion.default=1

声明式HTTP客户端

Spring Boot 4HTTP Service Clients 提供了自动配置支持,允许我们用接口 + 注解声明远端调用(类似 Feign,但以 Spring 的风格)

比如使用HttpExchange注解,代码量减少60%。再也不用通过RestTemplateWebClient来手写 HTTP 调用的实现类:

java

@HttpExchange(url = "https://echo.zuplo.io")
public interface EchoService {
    @PostExchange("/post") 
    Map<String, Object> echo(@RequestBody Map<String, String> body);
}
java
                                            
@Service                                                            
public class EchoBusinessService {

    private final EchoService echoService;                           // 客户端接口作为依赖

    @Autowired                                                       // 构造器注入
    public EchoBusinessService(EchoService echoService) {
        this.echoService = echoService;
    }

    public Map<String,Object> echoOnce() {
        Map<String,String> payload = Map.of("message", "hello");
        return this.echoService.echo(payload);                      // 调用远端并返回结果
    }
}

大家可以细心品味,接口式客户端的声明与调用,非常类似于 Feign 的体验

Spring Boot 4 的自动配置会在运行时生成实现并使用底层 WebClient。具体注解名与语义以 Spring Framework 7/Boot4 的文档为准

弃用Undertow

之前国内软件行业掀起用Undertow替换tomcat的浪潮,但现在Spring Boot 4.0.0 移除了对 Undertow 支持

我们来看一下有哪些原因:

  1. 规范适配滞后:Undertow 未及时适配 Servlet 6.1 规范,无法满足 Jakarta EE 11 基线要求。
  2. 维护团队能力不足:Undertow 核心维护者仅 3-5 人,且存在人员流失,2023-2024 年核心功能迭代周期从 2 个月延长至 6 个月以上
  3. 适配成本过高:为兼容 Spring Boot 3.xJakarta EE 9+AOT 编译等特性,Spring Boot 团队为 Undertow 投入 30% 的兼容性测试资源,远超 Tomcat(5%)Jetty(8%)
  4. 性能优势消失:通过 JMeter 测试,Tomcat 10.1.18Undertow 2.3.10.Final 性能相差无几,在现代 JVM 优化下,Undertow 的 “低内存、高并发” 优势已不明显

虚拟线程支持

基于 Java 21的虚拟线程特性,Spring Boot 4.0.0 重构线程池模型,实现 “百万级并发” 支持:

  • 异步处理优化:在支付网关场景中,每秒请求处理量RPS1.2万提升至8.5万CPU占用率下降40%
  • 无缝启用:通过spring.threads.virtual.enabled=true全局启用,原有@Async注解无需修改
  • 监控增强:Actuator新增/virtual-threads端点,实时监控线程状态与阻塞事件
java
// 全局启用虚拟线程
spring:
  threads:
    virtual:
      enabled: true

// 异步任务无需修改
@Async
public CompletableFuture<String> fetchData() {
    return CompletableFuture.completedFuture("Data from virtual thread");
}

新版本也正式提供对 Java 25 的顶级支持(LTS 版本 Java 17 依然兼容)

可观测性增强:Micrometer + Tracing

Spring Boot 4 更新了 Micrometer 版本并加强了 observability(包含 tracing 与更丰富的 auto-config)。

接下来,我们在注册指标和在关键路径添加 tracing span

plain
@Component                                               
public class MyMetrics {

    private final Counter requestCounter;                           // 计数器字段
    private final Tracer tracer;                                    // tracer 字段

    @Autowired
    public MyMetrics(MeterRegistry registry, Tracer tracer) {       // 构造器注入:MeterRegistry 与 Tracer
        this.requestCounter = Counter.builder("myapp.requests")    // 创建名为 myapp.requests 的计数器
            .description("Number of requests processed")          
            .register(registry);                                  // 注册到 MeterRegistry
        this.tracer = tracer;                                      
    }

    public void onRequest() {
        this.requestCounter.increment();                          // 每次请求时计数器加一
        Span span = this.tracer.nextSpan().name("my.custom.span");
        try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) {
            // 在这个 try 块中进行你想要跟踪的逻辑
        } finally {
            span.end();                                            // 结束 span
        }
    }
}

迁移建议

最后说一下迁移建议,可以不用太着急升级,再等待其他配置组件都适配SpringBoot4.0先落脚 Spring Boot 3.5.x,再迈向 4.0 的云原生世界,方能以最小成本,平稳升级,享受虚拟线程、原生镜像带来的性能红利。

点击"在看"并关注我,获取更多前沿技术解读与实战干货!

如果你对SpringBoot4.0.0的发布,有任何看法,欢迎在评论区留言呀


参考资料:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes

作者:小牛呼噜噜

本文到这里就结束啦,感谢阅读,关注同名公众号:小牛呼噜噜,防失联+获取更多技术干货