跳至主要內容

Spring Boot 4.0.0 预览版新特性详解:深入解读 Spring Framework 7.0.0

DD编辑部原创Spring BootSpring Boot大约 7 分钟

Spring Boot 4.0.0 预览版新特性详解:深入解读 Spring Framework 7.0.0

你是否注意到创建新 Spring Boot 项目时出现的最新选项?Spring Boot 4.0.0 预览版现已发布,基于最新的 Spring Framework 7.0.0 🌱。这个版本引入了众多激动人心的新特性,不仅提升了开发效率,改善了空值安全性,还简化了 Web 应用程序的开发流程。本文将深入探讨这些重要变化,并提供完整的代码示例和单元测试,帮助你快速上手这个预览版本。让我们一起来探索吧!🔍

注意:作为预览版本,Spring Boot 4.0.0 尚未达到生产就绪状态。在关键业务应用中使用前,请务必进行充分测试。

1. 优雅的API版本控制 📚

Spring Framework 7.0.0 引入了强大的 API 版本控制支持,开发者可以通过 @RequestMapping 注解中的 version 参数来管理同一端点的多个版本。这一特性大大简化了 REST API 向后兼容性的维护工作。

示例

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class VersionedController {
    @RequestMapping(value = "/user", version = "1")
    public String getUserV1() {
        System.out.println("Version 1");
        return "Version 1";
    }
    @RequestMapping(value = "/user", version = "2")
    public String getUserV2() {
        System.out.println("Version 2");
        return "Version 2";
    }
}

工作原理

  • /api/user 的请求如果带有版本头(如 Accept: application/vnd.api.v1+json)会路由到 getUserV1 方法
  • 带有版本 2 的请求会路由到 getUserV2 方法
  • 这种方式保持了代码库的整洁性,避免了为不同版本重复创建端点

优势:简化了 API 演进过程,让开发者能够在不影响现有客户端的前提下轻松引入新功能 🌟。

2. 使用 BeanRegistrar 实现便捷的 Bean 注入 🛠️

Spring Framework 7.0.0 新增的 BeanRegistrar 接口支持灵活的编程式 Bean 注册,可以根据活动配置文件等条件动态注册多个 Bean。

示例

import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.BeanRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;

@Configuration
@Import(MyBeansRegistrar.class)
public class MyConfiguration {
}
class MyBeansRegistrar implements BeanRegistrar {
    @Override
    public void register(BeanRegistry registry, Environment env) {
        registry.registerBean("user", User.class);
        if (env.matchesProfiles("dev")) {
            registry.registerBean(Order.class, spec -> spec
                    .supplier(context -> new Order("order_001")));
        }
    }
}
class User {
    private String name;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
class Order {
    private String name;
    public Order(String name) { this.name = name; }
    public String getName() { return name; }
}

单元测试
为了验证 Bean 是否正确注册,这里使用 Spring Boot 的测试框架编写一个单元测试:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@ActiveProfiles("dev")
class MyConfigurationTest {
    @Autowired
    private ApplicationContext context;
    @Test
    void testUserBeanRegistration() {
        User user = context.getBean(User.class);
        assertNotNull(user, "User bean should be registered");
    }
    @Test
    void testOrderBeanRegistrationInDevProfile() {
        Order order = context.getBean(Order.class);
        assertNotNull(order, "Order bean should be registered in dev profile");
        assertEquals("order_001", order.getName(), "Order name should be order_001");
    }
}

工作原理

  • MyBeansRegistrar 无条件注册一个 User Bean,仅在 dev 配置文件激活时注册 Order Bean
  • 单元测试验证了当 dev 配置文件激活时,这两个 Bean 在应用程序上下文中都可正常使用

优势:简化了动态 Bean 注册流程,特别适用于具有特定配置文件的复杂应用场景 📦。

3. 使用 JSpecify 注解提升空值安全性 🛡️

Spring Framework 7.0.0 通过采用 JSpecify 注解(@Nullable@NonNull)来增强空值安全性,替代了已弃用的 JSR 305 注解。这些注解可以帮助 IntelliJ IDEA 2024 等 IDE 在开发阶段捕获潜在的空指针异常问题。

示例

import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

public class Person {
    private String name;
    public void setName(@NonNull String name) {
        this.name = name;
    }
    @Nullable
    public String getName() {
        return this.name;
    }
}

工作原理

  • @NonNull 确保 setName 中的 name 参数不能为 null,如果传递 null 值会触发 IDE 警告
  • @Nullable 表示 getName 方法可能返回 null,提醒开发者进行适当的空值处理
  • IntelliJ IDEA 2024 会针对空值安全违规显示警告或错误,从而提高代码可靠性

优势:通过在开发阶段提前发现问题,有效减少运行时 NullPointerException 异常,增强代码健壮性 🔐。

4. 使用 @ImportHttpServices 轻松创建 HTTP 代理 🌐

新增的 @ImportHttpServices 注解简化了为 HTTP 接口创建代理的过程,支持以声明式方式对 HTTP 服务进行分组和配置。

示例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.RestClientHttpServiceGroupConfigurer;
import org.springframework.web.service.annotation.ImportHttpServices;

@Configuration(proxyBeanMethods = false)
@ImportHttpServices(group = "weather", types = {FreeWeather.class, CommercialWeather.class})
@ImportHttpServices(group = "user", types = {UserServiceInternal.class, UserServiceOfficial.class})
class HttpServicesConfiguration extends AbstractHttpServiceRegistrar {
    @Bean
    public RestClientHttpServiceGroupConfigurer groupConfigurer() {
        return groups -> groups.filterByName("weather", "user")
                .configureClient((group, builder) -> builder.defaultHeader("User-Agent", "My-Application"));
    }
}
interface FreeWeather {
    String getForecast();
}
interface CommercialWeather {
    String getDetailedForecast();
}
interface UserServiceInternal {
    String getUserDetails();
}
interface UserServiceOfficial {
    String getOfficialUserData();
}

工作原理

  • @ImportHttpServices 为指定的接口注册 HTTP 代理,分别归属于 weatheruser
  • RestClientHttpServiceGroupConfigurer 为组内所有服务应用通用配置,如请求头信息

优势:简化了与外部 HTTP 服务的集成流程,有效减少了样板代码 📡。

5. 其他值得关注的新特性 🎉

Spring Boot 4.0.0 和 Spring Framework 7.0.0 还包含了以下几个重要的增强功能:

a. SpEL 表达式升级 📝

SpEL 现在支持空值安全和 Elvis 操作符(?:),进一步简化了属性注入的过程。

示例

@Value("#{systemProperties['pop3.port'] ?: 25}")
private int port;

这行代码会注入 pop3.port 系统属性,如果该属性未定义则使用默认值 25

b. GraalVM 原生应用支持 ⚡

Spring AOT 技术可以将应用程序编译为原生镜像,显著减少启动时间和内存占用,特别适合云原生环境部署。

c. Jackson 3.x 支持 📊

对 Jackson 2.x 的支持已被弃用,Jackson 3.x(使用 tools.jackson 包)成为新的默认选择,提供了更优的性能和更丰富的功能。

d. Servlet 和 WebSocket 升级 🌐

Spring Framework 7.0.0 采用了 Jakarta Servlet 6.1 和 WebSocket 2.2 规范,需要使用现代容器,如 Tomcat 11+ 或 Jetty 12.1+。

e. HttpHeaders API 优化 🔍

HttpHeaders API 已完成现代化改造,新增了 firstValueforEach 等方法,使请求头操作更加便捷。

示例

@RestController
public class MyController {
    @GetMapping("/headers")
    public ResponseEntity<String> handleRequest(HttpHeaders headers) {
        String value = headers.firstValue("X-Custom-Header").orElse(null);
        headers.forEach((name, values) -> System.out.println(name + ": " + values));
        return ResponseEntity.ok("Processed");
    }
}

f. 已移除的功能 🗑️

  • Spring MVC 的 XML 配置已被弃用,推荐使用基于 Java 的配置方式
  • Spring TestContext Framework 中对 JUnit 4 的支持已被弃用
  • Spring JCL 已被移除,由 Apache Commons Logging 1.3.0 替代

g. 最低版本要求 📋

  • Jakarta EE 11 (Tomcat 11+)
  • Kotlin 2.x
  • JSONassert 2.0
  • GraalVM 23

完整的变更列表请参考 Spring Framework 7.0 发布说明open in new window

实际示例:在 Spring Boot 4.0.0 中构建版本化 API 🌟

下面我们通过一个完整的 Spring Boot 4.0.0 应用程序示例,来展示如何使用 API 版本控制和空值安全 Bean。

控制器

import org.jspecify.annotations.NonNull;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class UserController {
    private final UserService userService;
    public UserController(@NonNull UserService userService) {
        this.userService = userService;
    }
    @RequestMapping(value = "/user", version = "1")
    public String getUserV1() {
        return userService.getUserDetailsV1();
    }
    @RequestMapping(value = "/user", version = "2")
    public String getUserV2() {
        return userService.getUserDetailsV2();
    }
}
class UserService {
    public String getUserDetailsV1() {
        return "User Details (v1)";
    }
    public String getUserDetailsV2() {
        return "Enhanced User Details (v2)";
    }
}

配置

import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.BeanRegistry;
import org.springframework.core.env.Environment;

@Configuration
class AppConfig {
    @Bean
    public BeanRegistrar userServiceRegistrar() {
        return (registry, env) -> registry.registerBean("userService", UserService.class);
    }
}

测试

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;
    @Test
    void testGetUserV1() throws Exception {
        mockMvc.perform(get("/api/user").header("Accept", "application/vnd.api.v1+json"))
               .andExpect(status().isOk())
               .andExpect(content().string("User Details (v1)"));
    }
    @Test
    void testGetUserV2() throws Exception {
        mockMvc.perform(get("/api/user").header("Accept", "application/vnd.api.v2+json"))
               .andExpect(status().isOk())
               .andExpect(content().string("Enhanced User Details (v2)"));
    }
}

这个示例完整演示了 API 版本控制和 Bean 注册的功能,并通过单元测试验证了其正确性。

总结 📚

基于 Spring Framework 7.0.0 的 Spring Boot 4.0.0 预览版带来了众多革命性的新特性:

  • API 版本控制:通过 @RequestMapping 实现优雅的端点版本管理
  • BeanRegistrar:支持灵活的编程式 Bean 注册
  • 空值安全:采用 JSpecify 注解构建更健壮的代码
  • HTTP 代理:使用 @ImportHttpServices 简化服务集成
  • 技术栈现代化:全面支持 Jackson 3.x、Servlet 6.1、WebSocket 2.2 和 GraalVM 原生镜像

更多升级信息,请参考官方文档:

https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-7.0-Release-Notesopen in new window

最近我们翻译了Spring Boot和Spring AI的中文文档,有需要的小伙伴可以收藏,后续Spring Boot 4.0发布我们也会第一时间进行翻译

这些变化让 Spring Boot 4.0.0 成为构建现代云原生应用的强大选择。建议在你的项目中尝试这个预览版本,但在生产环境中请务必谨慎使用。欢迎在评论区分享你的使用体验,如果这篇文章对你有帮助,请不要忘记点赞支持 👏!

感谢你耐心阅读这篇文章!如果内容对你有所帮助,请点赞 👏、收藏 ⭐ 并分享给需要的朋友。

上次编辑于:
贡献者: didi