Spring Boot 4 與 Spring Framework 7 – 新功能
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 仍是最低要求。強烈建議使用 Java 21 和 Java 25,以便充分利用虛擬執行緒等新的 JVM 特性。我們可以在 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
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
中直接注意到這個變化。如果我們使用啟動依賴項,則無需進行任何變更。例如,當我們需要將 JPA 與 Hibernate 結合使用時,只需將此依賴項新增至pom.xml
中即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
差異在於:JPA 自動配置、Hibernate 整合和驗證設定現在是單獨模組的一部分,這使得框架在執行時間或 AOT 編譯期間處理配置時更具選擇性。
如果我們不使用啟動依賴項,則必須處理這些變更。我們可以在Spring Boot GitHub Wiki中找到有關新模組的詳細資訊。
3.5. 新的 @ConfigurationPropertiesSource 註解
另一個有助於更好的模組化功能是名為@ConfigurationPropertiesSource
的新註解。此註解不會改變配置屬性在執行時的綁定方式。相反,它只是在建置時為spring-boot-configuration-processor
提供提示。
當處理器為@ConfigurationProperties
類別產生元資料時,它通常會從定義該類別的相同模組收集資訊。然而,在模組化專案中,我們有時會依賴位於不同模組中的嵌套類型或基類,而這些模組的原始程式碼在建置時不可用。在這種情況下,產生的元資料可能不完整—例如,屬性描述或預設值可能缺失。
透過使用@ConfigurationPropertiesSource
標記一個類,我們可以指示處理器為其產生完整的元數據,即使該類未直接使用@ConfigurationProperties
註解。實際上,這意味著我們在跨模組工作時不再需要擔心元資料遺失。處理器會幫我們處理好這一切。
4. Spring 框架 7
Spring Framework 7 帶來了一系列期待已久的功能,並在測試、API 設計和核心基礎架構方面進行了全面的改進。這些改進不僅提升了框架的現代化程度,也減少了日常開發的樣板程式碼。
4.1. 測試改進
Spring 在測試過程中使用上下文快取來在測試效能和隔離性之間找到平衡。我們可以在本文中找到有關此操作的詳細資訊以及由此產生的陷阱和可能的解決方案。
Spring Framework 7 引入了測試上下文暫停功能。此前,長生命週期整合測試即使在空閒時也會消耗資源。現在,Spring 可以暫停和恢復儲存在上下文快取中的上下文,從而節省記憶體並加快大型套件中的測試執行速度。這對於 JMS 監聽器容器或計劃任務等應用非常有用。
此外,新的RestTestClient
讓測試類似於WebTestClient
REST 端點變得更加容易,但無需引入反應式基礎設施:
這使得 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 註解註解 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
Bean
在早期的 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 互通性,降低了大型程式碼庫中出現 NullPointerExceptions 的風險。
5. 棄用和移除
現代化帶來清理工作:
javax.*
套件已消失 - 僅支援 Jakarta EE 11。Jackson 2.x 支援已取消;Spring 7 預計支援 Jackson 3.x。
Spring JCL(日誌橋)已被刪除,取而代之的是 Apache Commons Logging。
JUnit 4 支援已棄用 — 僅使用 JUnit 5。
如果我們仍然依賴這些較舊的 API,則遷移應該是我們升級計劃的一部分。
6. 結論
Spring Boot 4 和 Spring Framework 7 不只是增量版本。它們是 Java 開發邁向現代、模組化、雲端原生時代的一步:
API 版本控制和彈性註釋使應用程式更容易發展和強化。
JSpecify 空安全性和 Kotlin 支援以減少執行階段錯誤。
聲明式 HTTP 用戶端簡化了服務到服務的呼叫。
原生影像支援和可觀察性工具提高了雲端就緒性。
與以往一樣,進行重大升級的關鍵在於儘早開始測試我們的應用程序,尤其是在依賴項升級和棄用 API 方面。但考慮到生產力、效能和可維護性方面的優勢,這種轉變是值得的。
與往常一樣,本文中提供的程式碼可在 GitHub 上找到。