SpringBoot4与Spring7发布,云原生支持达到全新高度

Spring Boot 4和Spring Framework 7带来基础要求升级、模块化改进、API版本化、声明式HTTP客户端、弹性注解等重大特性,标志着Java开发生态向云原生时代的深度进化。

近日,Spring生态迎来了自2022年以来最具里程碑意义的更新——Spring Boot 4和Spring Framework 7的正式发布。这两个版本延续了Spring团队的现代化征程,不仅采纳了最新的Java语言特性,还与Jakarta EE 11实现了更紧密的整合,同时显著提升了开发效率并为应用提供了更强的弹性支持。本文将深入解析这两个版本的核心特性,帮助开发者全面把握这次升级带来的变革。

基础要求全面升级

Spring Boot 4和Spring Framework 7在基础要求上有了显著提升。虽然Java 17仍然是最低要求,但官方强烈推荐使用Java 21或Java 25以充分利用虚拟线程等新JVM特性。Spring Framework 7全面采用了Jakarta EE 11标准,这意味着开发者将使用Servlet 6.1、JPA 3.2和Bean Validation 3.1等最新规范。对于Kotlin开发者,新版本支持Kotlin 2.2及以上版本,提供了更流畅的协程集成和响应式代码编写体验。

Spring Boot 4的重大改进

作为第四个主要版本,Spring Boot 4在性能、可观察性、可维护性和配置支持方面都有显著增强。

在本地镜像方面,Spring Boot 4与GraalVM 24完全对齐,提前处理技术得到增强,这意味着更快的构建时间和更少的启动内存占用。Spring Data引入了AOT存储库,将查询方法转换为与应用程序一起编译的源代码。

可观察性方面,Spring Boot 4升级到Micrometer 2并集成了OpenTelemetry starter,使追踪、日志和指标能够无缝协作。SSL健康报告也得到了改进,证书链中即将过期的证书现在会在新的expiringChains条目中显示,不再使用WILL_EXPIRE_SOON状态,而是将即将过期的证书报告为VALID,这使得团队在生产环境中监控SSL证书有效性变得更加容易。

模块化是Spring Boot 4的另一个重要改进。新版本将自动配置和支持代码拆分为更小、更专注的模块,这意味着更快的构建和本地镜像生成,更清晰的依赖管理,以及为Spring团队和贡献者提供了更好的可维护性。

对于使用starter依赖的开发者,不需要任何更改。例如,当需要JPA与Hibernate时,只需在pom.xml中添加以下依赖:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>


区别在于底层实现:JPA自动配置、Hibernate集成和验证设置现在位于单独的模块中,这使得框架在运行时或AOT编译期间处理配置时能够更有选择性。

新增的@ConfigurationPropertiesSource注解为更好的模块化提供了支持。这个注解在构建时为spring-boot-configuration-processor提供提示,确保即使在跨模块工作时也能生成完整的元数据。

Spring Framework 7的创新特性

Spring Framework 7带来了许多期待已久的功能和精心设计的改进,涵盖了测试、API设计和核心基础设施等方面。

测试方面引入了测试上下文暂停功能。之前,长期运行的集成测试即使在空闲时也会消耗资源。现在,Spring可以暂停和恢复存储在上下文缓存中的上下文,从而在大型测试套件中节省内存并加快测试执行速度。新的RestTestClient使得测试REST端点变得更加简单,类似于WebTestClient,但不需要引入响应式基础设施。

以下是一个使用RestTestClient的示例:

java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class HelloWorldApiIntegrationTest {
    RestTestClient client;
    
    @BeforeEach
    void setUp(WebApplicationContext context) {
        client = RestTestClient.bindToApplicationContext(context)
            .build();
    }
    
    @Test
    void shouldFetchHelloV1() {
        client.get()
            .uri("/api/v1/hello")
            .exchange()
            .expectStatus()
            .isOk()
            .expectHeader()
            .contentTypeCompatibleWith(MediaType.TEXT_PLAIN)
            .expectBody(String.class)
            .consumeWith(message -> assertThat(message.getResponseBody()).containsIgnoringCase("hello"));
    }
}

API版本化是其中一个最受期待的新功能。传统上,开发者必须通过URL路径约定、自定义头或媒体类型来实现自己的解决方案。现在,框架提供了原生支持,开发者可以在控制器或方法级别指定版本属性:

java
@RestController
@RequestMapping("/hello")
public class HelloWorldController {
    @GetMapping(version = "1", produces = MediaType.TEXT_PLAIN_VALUE)
    public String sayHelloV1() {
        return "Hello World";
    }
    
    @GetMapping(version = "2", produces = MediaType.TEXT_PLAIN_VALUE)
    public String sayHelloV2() {
        return "Hi World";
    }
}

也可以在控制器级别指定版本:

java
@RestController
@RequestMapping(path = "/hello", version = "3")
public class HelloWorldV3Controller {
    @GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
    public String sayHello() {
        return "Hey World";
    }
}

然后需要配置映射策略,可以是基于路径的映射、基于查询参数的映射、基于请求头的映射或基于媒体类型头的映射。以下配置使用基于路径的映射:

java
@Configuration
public class ApiConfig implements WebMvcConfigurer {
    @Override
    public void configureApiVersioning(ApiVersionConfigurer configurer) {
        configurer.usePathSegment(1);
    }
    
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("/api/v{version}", HandlerTypePredicate.forAnnotation(RestController.class));
    }
}

声明式HTTP客户端支持是另一个值得注意的特性。受Feign启发但更轻量且完全集成,新的@HttpServiceClient注解使得创建HTTP客户端变得更加简单:

java
@HttpServiceClient("christmasJoy")
public interface ChristmasJoyClient {
    @GetExchange("/greetings?random")
    String getRandomGreeting();
}

然后需要激活类路径扫描并配置客户端分配到的服务组:

java
@Configuration
@Import(HttpClientConfig.HelloWorldClientHttpServiceRegistrar.class)
public class HttpClientConfig {
    static class HelloWorldClientHttpServiceRegistrar extends AbstractClientHttpServiceRegistrar {
        @Override
        protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) {
            findAndRegisterHttpServiceClients(registry, List.of("com.baeldung.spring.mvc"));
        }
    }
    
    @Bean
    RestClientHttpServiceGroupConfigurer christmasJoyServiceGroupConfigurer() String baseUrl) {
        return groups -> {
            groups.filterByName("christmasJoy")
                .forEachClient((group, clientBuilder) -> {
                    clientBuilder.baseUrl("https://christmasjoy.dev/api");
                });
        };
    }
}

ChristmasJoyClient然后可以像往常一样注入到其他Spring组件中:

java
@RestController
@RequestMapping(path = "/hello", version = "4")
@RequiredArgsConstructor
public class HelloWorldV4Controller {
    private final ChristmasJoyClient christmasJoy;
    
    @GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
    public String sayHello() {
        return this.christmasJoy.getRandomGreeting();
    }
}

弹性注解现在内置在框架中。开发者可以用Spring注解来装饰Spring组件方法,直接添加重试逻辑或并发限制:

java
@HttpServiceClient("christmasJoy")
public interface ChristmasJoyClient {
    @GetExchange("/greetings?random")
    @Retryable(maxAttempts = 3, delay = 100, multiplier = 2, maxDelay = 1000)
    @ConcurrencyLimit(3)
    String getRandomGreeting();
}

这些注解默认被忽略,除非在配置中添加@EnableResilientMethods。

多个TaskDecorator bean的支持消除了手动组合装饰器的需要。例如,当有异步事件监听器时:

java
@Component
@Slf4j
public class HelloWorldEventLogger {
    @Async
    @EventListener
    void logHelloWorldEvent(HelloWorldEvent event) {
        log.info("Hello World Event: {}", event.message());
    }
}

可以简单注册两个TaskDecorator bean:

java
@Configuration
@Slf4j
public class TaskDecoratorConfiguration {
    @Bean
    @Order(2)
    TaskDecorator loggingTaskConfigurator() {
        return runnable -> () -> {
            log.info("Running Task: {}", runnable);
            try {
                runnable.run();
            } finally {
                log.info("Finished Task: {}", runnable);
            }
        };
    }
    
    @Bean
    @Order(1)
    TaskDecorator measuringTaskConfigurator() {
        return runnable -> () -> {
            final var ts1 = System.currentTimeMillis();
            try {
                runnable.run();
            } finally {
                final var ts2 = System.currentTimeMillis();
                log.info("Finished within {}ms (Task: {})", ts2 - ts1, runnable);
            }
        };
    }
}

空安全方面采用了JSpecify作为标准:

java
@Configuration
public class ApiConfig implements WebMvcConfigurer {
    @Override
    public void configureApiVersioning(@NonNull ApiVersionConfigurer configurer) {
        configurer.usePathSegment(1);
    }
}

弃用和移除的功能

随着现代化进程,一些旧功能被清理:javax.*包被完全移除,只支持Jakarta EE 11;放弃了对Jackson 2.x的支持,Spring 7期望使用Jackson 3.x;移除了Spring JCL日志桥接,转而采用Apache Commons Logging;弃用了JUnit 4支持,建议 exclusively 使用JUnit 5。如果仍然依赖这些较旧的API,迁移应该成为升级计划的一部分。


这些更新使得应用程序更容易演进和强化,通过JSpecify空安全和Kotlin支持减少运行时错误,声明式HTTP客户端简化了服务间调用,本地镜像支持和可观察性工具提高了云就绪程度。虽然重大升级总是需要投入测试工作,但在生产力、性能和可维护性方面的收益使得过渡是值得的。