Spring Boot 4 和 Spring Framework 7 – 有什么新功能
上次更新:2025年11月14日
1. 概述
在2022年末,Spring Boot 3 和 Spring Framework 6带来了自其诞生以来生态系统中最重大的转变。 它们引入了 Java 17 基线,从 javax.* 迁移到 jakarta.*,以及对 GraalVM 原生镜像的早期支持。
现在,在 2025 年,下一代产品即将到来:Spring Boot 4 和 Spring Framework 7。
这两个版本都继续推进现代化进程。它们采用了最新的 Java 语言特性,并提供了更紧密的 Jakarta EE 11 对齐。它们还提高了开发人员的生产力,并开箱即用地提供可靠的应用支持。
在本文中,我们将深入探讨这些版本的主要主题,并提供解释和代码示例,重点介绍开发人员可以期待的内容。
2. 基线升级
在深入研究功能之前,重要的是要注意新的基线。
由于目前在行业内广泛采用,Java 17 仍然是最低要求。为了充分利用新的 JVM 特性,如虚拟线程,强烈建议使用 Java 21 和 Java 25。 可以在 Spring 博客的官方声明中找到。
对于 Spring Framework 7,Jakarta EE 11 现在已完全采用。 这意味着我们将升级到 Servlet 6.1、JPA 3.2 和 Bean Validation 3.1。
在 Kotlin 方面,现在支持版本 2.2 及以上。 这带来了更流畅的协程集成,并使使用反应式代码感觉更加自然。
3. Spring Boot 4
在第四个主要版本中,Spring Boot 带来了多项改进。 它增强了性能、可观察性、可维护性和配置支持。这些变化进一步巩固了它作为现代云原生 Java 应用的基础。
3.1. 原生镜像改进
Spring Boot 4 继续大力推动 GraalVM 原生镜像支持。 它与 GraalVM 24 完全对齐。 提前编译 (AOT) 处理得到了增强,这意味着更快的构建时间和更小的启动内存占用。
例如,Spring Data 引入了 AOT Repositories,即 AOT 处理会将查询方法转换为与应用程序一起编译的源代码。
3.2. 可观察性:Micrometer 2 和 OpenTelemetry
云原生应用程序依赖于良好的可观察性。Spring Boot 3 引入了 Spring Observability。 Spring Boot 4 升级到 Micrometer 2 并集成了 OpenTelemetry starter。 这使得追踪、日志和指标能够无缝协同工作。
3.3. 改进的 SSL 健康状况报告
如果证书链包含即将到期的证书,我们现在可以在一个新的 expiringChains 条目中看到它们。 WILL_EXPIRE_SOON 状态已被移除。相反,到期证书被报告为 VALID。
这些变化使团队能够更轻松地监控生产环境中的 SSL 证书有效性,而不会出现误报。
3.4. 模块化
Spring Boot 4 的第一个里程碑之一是对其自身代码库进行重构,使其成为更模块化的结构.
在 Spring Boot 3 中,许多核心模块(如自动配置、启动依赖和构建工具)被打包在更大的工件中。虽然方便,但这有时会使依赖项管理变得更加困难,导致类路径扫描开销增加,并增加了原生镜像的占用空间。
从 Spring Boot 4 开始,团队已经开始将自动配置和支持代码拆分为更小、更集中的模块。 这种内部模块化意味着
-
更快的构建和原生镜像生成。 GraalVM AOT 处理不需要处理不必要的提示和元数据。
-
更清晰的依赖管理。可选的集成(如 Micrometer、OpenTelemetry 或特定的持久化技术)位于单独的模块中,而不是捆绑在一起。
-
Spring 团队和贡献者更易于维护。模块更直接地映射到功能。
作为开发者,我们可能不会直接在 pom.xml 或 build.gradle 中注意到此更改。如果我们使用 starter 依赖项,则无需进行任何更改。例如,当我们需要 JPA 与 Hibernate 时,只需将此依赖项添加到我们的 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
区别在于底层实现:JPA 自动配置、Hibernate 集成和验证设置现在是单独模块的一部分,这使得框架在运行时或 AOT 编译期间处理配置时可以更具选择性。
如果我们不使用 starter 依赖项,则需要注意这些更改。有关新模块的详细信息,请参阅 Spring Boot GitHub Wiki。
3.5. 新的 @ConfigurationPropertiesSource 注解
另一个用于更好模块化的功能是名为 @ConfigurationPropertiesSource 的新注解。该注解不会改变配置属性在运行时绑定的方式。相反,它充当一个提示,供spring-boot-configuration-processor 在构建时使用。
当处理器为 @ConfigurationProperties 类生成元数据时,通常会从定义该类的模块中收集信息。然而,在模块化项目中,我们有时依赖于嵌套类型或基类,这些类型或基类位于不同的模块中,在构建时无法获取源代码。在这种情况下,生成的元数据可能是不完整的——例如,属性描述或默认值可能缺失。
通过使用 @ConfigurationPropertiesSource 注解标记一个类,我们指示处理器为其生成完整的元数据,即使它未直接使用 @ConfigurationProperties 注解。实际上,这意味着我们不再需要担心跨模块时元数据缺失的问题。 处理器会为我们处理它。
4. Spring Framework 7
Spring Framework 7 带来了长期以来要求的特性和对测试、API 设计和核心基础设施的深思熟虑的改进。这些变化使框架现代化,并减少了日常开发的冗余代码。
4.1. 测试改进
Spring 在测试期间使用上下文缓存,以在测试性能和隔离性之间找到平衡。有关这些以及由此产生的陷阱和可能的解决方案,请参阅 本文。
Spring Framework 7 引入了测试上下文暂停。 以前,长时间运行的集成测试即使在空闲时也会消耗资源。现在,Spring 可以暂停和恢复存储在上下文缓存中的上下文,从而节省内存并加快大型测试套件的测试执行速度。这对于 JMS 监听器容器或计划任务非常有用。
此外,一个新的 RestTestClient 使测试 REST 端点更容易,类似于 WebTestClient,但不需要引入响应式基础设施
这使得 REST 测试更接近 WebTestClient 的简单性,而无需依赖于响应式依赖项
@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"));
}
}
4.2. API 版本控制
最受欢迎的新功能之一是首个 API 版本控制。
传统上,我们不得不自己编写解决方案——通过 URL 路径约定 (/v1/)、自定义标头或媒体类型。现在,框架提供了原生支持。我们可以指定一个 version 属性,如本示例所示
@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";
}
}
我们也可以在控制器级别指定版本
@RestController
@RequestMapping(path = "/hello", version = "3")
public class HelloWorldV3Controller {
@GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
public String sayHello() {
return "Hey World";
}
}
然后,我们需要配置映射策略,可以是以下之一
- 基于路径的映射(例如 /api/v1/hello 与 /api/v2/hello)
- 基于查询参数 (例如 /hello?version=1 与 /hello?version=2)
- 基于请求头 (例如 X-API-Version: 1 与 X-API-Version: 2)
- 基于媒体类型头 (例如 Accept: application/json; version=1 与 Accept: application/json; version=2)
以下配置使用基于路径的映射
@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));
}
}
Spring 将自动解析版本。这使得演进 API 而不破坏现有客户端变得容易得多。
4.3. 带有 @HttpServiceClient 的更智能 HTTP 客户端
另一个值得注意的功能是声明式的 HTTP 客户端支持。灵感来自 Feign,但更轻量级且完全集成。
在旧版本的 Spring 中,我们需要 为 HttpInterface 创建一个代理。 也可以实现更智能的解决方案,但需要单独构建。 例如,在这个仓库 中,我们可以找到一个带有自定义 @HttpClient 注解和自定义 Bean Registrar 的示例(正如我们在 这篇文章 中看到的,它也随着 Spring Framework 7 的改进)。
现在,我们拥有内置的解决方案,即 @HttpServiceClient 注解。 让我们看一个例子
@HttpServiceClient("christmasJoy")
public interface ChristmasJoyClient {
@GetExchange("/greetings?random")
String getRandomGreeting();
}
然后,我们需要激活类路径扫描并配置客户端所属的服务组
@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 组件中
@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();
}
}
4.4. 弹性注解
Spring Retry 已经成为生态系统的一部分多年了,但它一直感觉像一个“附加组件”。 在 Spring Framework 7 中,弹性现在是内置的。 我们可以使用 Spring 注解 来直接添加重试逻辑或并发限制
@HttpServiceClient("christmasJoy")
public interface ChristmasJoyClient {
@GetExchange("/greetings?random")
@Retryable(maxAttempts = 3, delay = 100, multiplier = 2, maxDelay = 1000)
@ConcurrencyLimit(3)
String getRandomGreeting();
}
除非我们在配置之一中添加 @EnableResilientMethods,否则这些注解将被忽略。
这大大简化了添加弹性模式,而无需像 Resilience4j 这样的额外库,尽管它们仍然可以很好地集成。
这使得在运行时验证弹性策略变得容易得多,确保我们的注解实际被应用。
4.5. 多个 TaskDecorator Beans
在较早的 Spring 版本中,当我们想要自定义异步任务的执行时,可以在 ThreadPoolTaskExecutor 上注册单个 TaskDecorator。 这允许我们,例如,将 SecurityContext 或日志 MDC 传播到异步线程中。 但是,如果我们需要应用多个关注点,则必须手动创建一个组合装饰器。
从 Spring Framework 7 开始,我们现在可以在应用程序上下文中声明多个 TaskDecorator bean。 Spring 将自动将它们组合成一个链。 每个装饰器都会依次应用,按照它们的 bean 定义或 @Order 注解的顺序。
例如,我们有一个异步事件监听器
@Component
@Slf4j
public class HelloWorldEventLogger {
@Async
@EventListener
void logHelloWorldEvent(HelloWorldEvent event) {
log.info("Hello World Event: {}", event.message());
}
}
当我们想要简单的日志记录和时间戳测量时,可以简单地注册两个 TaskDecorator bean
@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);
}
};
}
}
生成的日志输出如下:
Running Task: com.baeldung.spring.mvc.TaskDecoratorConfiguration$$Lambda/0x00000ff0014325f8@57e8609
Hello World Event: "Happy Christmas"
Finished within 0ms (Task: java.util.concurrent.FutureTask@bb978d6[Completed normally])
Finished Task: com.baeldung.spring.mvc.TaskDecoratorConfiguration$$Lambda/0x00000ff0014325f8@57e8609
这项改进消除了对样板组合装饰器的需要,并使在异步代码中组合多个横切关注点变得更容易。
4.6. 使用 JSpecify 的空安全
空值注解在 Java 生态系统中无处不在 (@Nonnull, @Nullable, @NotNull 等)。 在 Spring Framework 7 中,团队采用 JSpecify 作为标准
@Configuration
public class ApiConfig implements WebMvcConfigurer {
@Override
public void configureApiVersioning(@NonNull ApiVersionConfigurer configurer) {
configurer.usePathSegment(1);
}
}
这改进了 IDE 工具和 Kotlin 互操作性,降低了较大代码库中 NullPointerException 的风险。
5. 弃用和删除
现代化伴随着清理
-
javax.* 包已消失 — 仅支持 Jakarta EE 11。
-
Jackson 2.x 支持已被删除; Spring 7 需要 Jackson 3.x。
-
Spring JCL(日志桥接)已被移除,转而支持 Apache Commons Logging。
-
完全移除了 JUnit 4 的支持——Spring Boot 4 和 Spring Framework 7 现在强制要求使用 JUnit Jupiter 6。
如果仍然依赖这些较旧的 API,迁移应包含在我们的升级计划中。
6. 结论
Spring Boot 4 和 Spring Framework 7 并非简单的增量发布。 它们是向 Java 开发的现代、模块化、云原生时代迈出的深思熟虑的一步。
-
API 版本控制和弹性注解使应用程序更易于演化和强化。
-
JSpecify 空安全和 Kotlin 支持,以减少运行时错误。
-
声明式 HTTP 客户端简化了服务间的调用。
-
原生镜像支持和可观察性工具改进了云就绪性。
与所有重大升级一样,关键在于尽早开始测试我们的应用程序,尤其是在依赖项升级和弃用 API 方面。但生产力、性能和可维护性方面的优势使过渡值得。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。
















