Autor artykułu:
Abdelaziz LOUARIT,
programista Java w MARGO.
Wprowadzenie
Spring Boot 4 to ważny kamień milowy dla nowoczesnego ekosystemu Java.
Po kilku latach stabilizacji wokół Spring Boot 3 nowa wersja realizuje trzy główne cele: uproszczenie pracy programistów, utrwalenie dobrych praktyk cloud-native oraz dalsze skrócenie czasu uruchamiania i poprawę obserwowalności.
Oparty na Spring Framework 7.x, rozwija osiągnięcia poprzednich wersji, a jednocześnie wprowadza natywne funkcje poświęcone odporności, obserwowalności i wydajności.
Dostosowany do Javy 17 (LTS), która staje się nowym minimalnym wymaganiem, oraz do Jakarta EE 11, Spring Boot 4 jest wyraźnie ukierunkowany na aplikacje cloud-native i serverless. Wyróżnia się wyraźnym ograniczeniem zależności zewnętrznych, wbudowanym null-safety dzięki JSpecify oraz zoptymalizowaną obsługą kompilacji AOT (Ahead-of-Time) i obrazów natywnych GraalVM.
Ta wersja stawia na prostotę i spójność: mniej niejawnej konfiguracji, bardziej przewidywalne zachowanie i lepsze przygotowanie do środowisk kontenerowych, w których liczy się szybki start.
W porównaniu ze Spring Boot 3 (Java 17 / Jakarta EE 10) oficjalne benchmarki wskazują na skrócenie czasu uruchamiania w trybie natywnym nawet o 50% oraz zmniejszenie zużycia pamięci o 30–40%.
- najważniejsze nowości i ich praktyczne wdrożenie;
- szczegółowe porównanie ze Spring Boot 3;
- przewodnik migracji krok po kroku;
- wyczerpującą checklistę migracji;
- rozbudowane techniczne FAQ.
Funkcje gotowe do produkcji
Wbudowana odporność: @Retryable i @ConcurrencyLimit
Odporność, wcześniej obsługiwana przez Spring Retry (lub Resilience4j), jest teraz natywną funkcją Spring Boot 4. Te adnotacje wykorzystują Project Reactor do strategii backoff i integrują Micrometer na potrzeby monitoringu.
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.springframework.service.annotation.ConcurrencyLimit; // New in Spring Boot 4
@Service
public class PaymentService {
@Retryable(
maxAttempts = 4, // Includes the initial attempt
backoff = @Backoff(delay = 1000, multiplier = 2.0) // Exponential backoff with jitter
)
@ConcurrencyLimit(value = 5)
public void processPayment(String paymentId) {
// Logic: External call (e.g., RestTemplate or WebClient)
externalApi.call(paymentId);
}
}
- automatycznie ponawiać wywołania w razie przejściowych awarii,
- ograniczać ruch (throttling/rate limiting): limitować liczbę równoczesnych wywołań krytycznego endpointu.
Natywne wersjonowanie API
Wersjonowanie jest teraz jawnie określane w mapowaniach, więc nie trzeba duplikować kontrolerów ani tworzyć zbędnych ścieżek. To ogranicza dług techniczny i porządkuje ewolucję kontraktów API.
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping(path = "/users", version = "1", produces = "application/vnd.app.v1+json")
public List getUsersV1() { return List.of(new UserV1("Alice", "ADMIN")); }
@GetMapping(path = "/users", version = "2", produces = "application/vnd.app.v2+json")
public List getUsersV2() { return List.of(new UserV2("Alice", "ADMIN", 30)); } // Added 'age' field
}
Wystarczy ustawić spring.mvc.versioning.strategy=header w pliku application.yml.
spring:
mvc:
versioning:
strategy: header
Null‑safety (JSpecify)
Adnotacje nullowalności czynią kontrakty jawnymi: parametry obowiązkowe, wartości opcjonalne i typy zwracane, które mogą przyjmować null. IDE wykorzystują je, aby zapobiegać wyjątkom NPE (NullPointerException) i podpowiadać przy autouzupełnianiu.
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
public final class EmailService {
public void send(@NonNull String to, @Nullable String cc, @NonNull String body) {
java.util.Objects.requireNonNull(to); // Compile-time check via plugins
// ...
}
}
Wydajność: AOT i buildy natywne (GraalVM)
Szybszy start i mniejsze zużycie pamięci stały się celami pierwszorzędnymi. Spring Boot 4 wzmacnia integrację z AOT i upraszcza korzystanie z GraalVM. AOT (Ahead-of-Time) kompiluje beany na etapie budowania, skracając czas uruchamiania nawet o 90% w trybie natywnym. Rozszerzona konfiguracja Maven:
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.10.3</version> <!-- Version aligned with Boot 4 -->
<executions>
<execution>
<goals><goal>compile</goal><goal>build</goal></goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>myapp</imageName>
<buildArgs>--no-fallback --initialize-at-build-time</buildArgs>
</configuration>
</plugin>
Rozbudowany Actuator (readiness/liveness, SSL, Mongo)
Endpointy health są bardziej ustandaryzowane: sondy readiness/liveness są domyślnie włączone, a nowe wskaźniki ułatwiają monitoring i integrację z Kubernetes.
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
probes:
enabled: true
show-details: always
ssl:
enabled: true
Jackson 3 i Jakarta EE 11
Przejście na Jackson 3 i pełna migracja do `jakarta.*` modernizują ekosystem. Efekt uboczny: niektóre biblioteki zewnętrzne wymagają aktualizacji. Uwzględnij to na wczesnym etapie migracji.
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Email;
public record CreateUserRequest(@NotBlank String name, @Email String email) {}
Migracja do Spring Boot 4
Szybkie porównanie: Spring Boot 3 → 4
| Funkcja | Spring Boot 3.x | Spring Boot 4.x |
|---|---|---|
| Minimalna wersja Javy | 17 | 17 |
| Wiązanie JSON | Jackson 2.15 | Jackson 3.0: natywna obsługa rekordów i wydajniejsza serializacja |
| Odporność | Przez zewnętrzną zależność Spring Retry | Wbudowana: @Retryable, @ConcurrencyLimit |
| Null-safety | Opcjonalnie przez biblioteki zewnętrzne | Zintegrowane (JSpecify) |
| Specyfikacje EE | Jakarta EE 10 | Jakarta EE 11 (pełna migracja) |
| AOT / GraalVM | Buildy eksperymentalne / niestabilne | Ustabilizowane: 2x szybszy start, –40% zużycia pamięci |
| Sondy Actuatora | Konfiguracja ręczna | Domyślnie włączone (gotowe na Kubernetes) |
| Obserwowalność | Podstawowa (Micrometer, Prometheus) | Pełna integracja z OpenTelemetry (OTel): ślady, logi, metryki |
| Pakowanie / build | Standardowy obraz Docker | Natywna obsługa GraalVM i uproszczone narzędzia buildu Maven |
| Wycofania (deprecations) | Nieliczne (np. @ConstructorBinding) | Wzbogacone metadane, lepsze autouzupełnianie w IDE |
| Częstotliwość aktualizacji | Niska | Wysoka |
Migracja do Spring Boot 4 krok po kroku
Na podstawie oficjalnego przewodnika: Migration Guide – Spring Boot 4.0
Faza 1 — przygotowanie
- Przejdź na Javę 17 (lub nowszą) i Spring Boot 3.3.5; usuń istniejące wycofane elementy (deprecations).
- Zidentyfikuj starsze zależności: uruchom ./mvnw dependency:tree | grep javax w celu wyszukania pozostałych zależności od javax.* w projekcie.
- Zrefaktoryzuj importy: użyj IDE, aby zamienić na przykład javax.validation na jakarta.validation.
- Zaktualizuj kluczowe komponenty: upewnij się, że sterowniki, startery i główne biblioteki są aktualne.
- Ustal punkt odniesienia: zmierz obecną wydajność (czas uruchamiania za pomocą time java -jar, zużycie pamięci za pomocą JFR), aby mieć bazę do porównań po migracji.
Faza 2 — testy wersji milestone i RC
- Osobna gałąź: utwórz gałąź migracyjną i przełącz BOM na 4.0.0-M*.
- Kompilacja i poprawki: zaktualizuj zależności i rozwiąż w IDE niekompatybilne zmiany (breaking changes) związane z Jakarta/Jackson.
- Stopniowe aktualizacje: refaktoryzuj importy krok po kroku, aktualizuj startery i uruchamiaj wszystkie testy.
- Porządki w API: zidentyfikuj i zastąp usunięte lub wycofane API.
- Walidacja: sprawdź zgodność z Jackson 3, Spring Security, Hibernate i serializacją JSON (zwłaszcza testami jednostkowymi i integracyjnymi).
- Metryki AOT: porównaj metryki AOT (start, pamięć) z punktem odniesienia, aby wykryć regresje.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>4.0.0-RC2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Faza 3 — przejście na wersję GA (General Availability)
- Przejdź na wersję 4.0.0 (GA).
- Uporządkuj zależności: usuń zbędne biblioteki (np. Spring Retry, jeśli korzystasz z natywnych adnotacji).
- Walidacja endpointów: ponownie zweryfikuj wystawione endpointy (Actuator, Security).
- Wdrożenie: wdróż na środowisko staging, monitoruj za pomocą Micrometer/Prometheus i stopniowo wprowadzaj zmiany na produkcję.
Faza 4 — optymalizacja
- Obserwowalność: zintegruj Micrometer, aby śledzić ponowienia, sukcesy i błędy (np. liczniki na @Retryable); eksportuj dane do Prometheus.
- Benchmark końcowy: porównaj wyniki przed i po. Zmierz zyski (start, pamięć za pomocą JFR/JMeter) względem początkowego punktu odniesienia i oblicz różnice procentowe, aby potwierdzić efekt.
- Dzielenie się wiedzą: udokumentuj wnioski i podziel się nimi z zespołem.
Checklista migracji przed wdrożeniem na produkcję
- Java 17 (lub nowsza) wdrożona we wszystkich środowiskach
- Brak pozostałości
javax.*w zależnościach - Biblioteki zaktualizowane pod Jackson 3 (HATEOAS, MapStruct, Lombok, Hibernate itp.)
- Właściwości YAML sprawdzone pod kątem ewentualnych zmian nazw
- Natywna odporność włączona / Spring Retry usunięty
- Actuator zweryfikowany (w tym aktywne sondy)
- Testy integracyjne i obciążeniowe zakończone powodzeniem
- AOT/GraalVM przetestowane, jeśli planujesz wdrożenie natywne
Słowniczek pojęć
- AOT (Ahead-Of-Time): kompilacja kodu Java przed wykonaniem w celu skrócenia czasu uruchamiania i zmniejszenia zużycia pamięci.
- GraalVM: zoptymalizowana maszyna wirtualna, która pozwala tworzyć natywne pliki wykonywalne i obsługuje wykonywanie wielojęzyczne (Java, Python itp.).
- OTel (OpenTelemetry): otwarty standard zbierania, śledzenia i centralizowania metryk, logów i śladów aplikacji.
Podsumowanie
Spring Boot 4 umacnia ekosystem Java w erze cloud-native: natywna odporność, zaawansowana obserwowalność dzięki OpenTelemetry (OTel) oraz optymalizacje AOT/GraalVM, które drastycznie zmniejszają zużycie zasobów (nawet –40% pamięci w trybie natywnym według oficjalnych benchmarków). Migrując krok po kroku, od przygotowania w wersji 3.3.x po dostrajanie, ograniczasz ryzyko i jednocześnie wdrażasz nowoczesne standardy, takie jak Java 21 (i jej wątki wirtualne), Jakarta EE 11, Jackson 3 oraz ujednoliconą obserwowalność.
Ostatecznie ta wersja sprawia, że Twoje aplikacje stają się bardziej przewidywalne, skalowalne i łatwiejsze w utrzymaniu, mają mniej zależności zewnętrznych i mocniej koncentrują się na logice biznesowej. Wysiłek wyraźnie się opłaca:
- większa odporność na przejściowe awarie (retry/backoff, limity współbieżności),
- ujednolicony monitoring (ślady, metryki, logi przez OTel) i ustandaryzowane health checki,
- wydajność gotowa na serverless (szybszy start, mniejsze zużycie zasobów, obrazy natywne).
Spring Boot 4 nie oznacza zerwania z przeszłością; to strategiczna aktualizacja: nowoczesny fundament dla trwałych, szybkich i obserwowalnych aplikacji Java – w pełni przygotowany na wymagania multi-cloud roku 2025 (i kolejnych lat).
Aby poszerzyć wiedzę o Javie i być na bieżąco, przeczytaj nasz artykuł Java 25: najważniejsze wnioski i nowości.
Czy migrować wszystkie usługi naraz?
Nie. Zalecamy podejście przyrostowe, oparte na domenach biznesowych (z wykorzystaniem wzorca Strangler). Zacznij od usług krytycznych lub nowych modułów, aby potwierdzić wzrost wydajności, jaki dają AOT i GraalVM.
Czy można pozostać na wersji 3.x?
Wsparcie OSS dla Spring Boot 3.3.x kończy się w czerwcu 2025 r., a Spring Boot 3.5.x jest wspierany do 30 czerwca 2026 r. Zaplanuj przejście z wyprzedzeniem, aby zapewnić:
- zgodność z Jackson 3, który staje się nowym standardem,
- gotowość na Jakarta EE 11 z myślą o przyszłej kompatybilności,
- a także bezpieczeństwo, ponieważ poprawki przestaną być wydawane po zamknięciu okna wsparcia LTS.
Czy AOT/GraalVM jest obowiązkowe?
Nie. Wybierz obciążenia, w których ROI jest udowodnione: serverless, edge computing lub często restartowane mikrousługi.
Czy null-safety wymaga dużej refaktoryzacji?
Nie. Zacznij od najbardziej krytycznych modułów i wdrażaj je stopniowo.
Jaki jest wpływ na Spring Cloud?
Zgodność ze Spring Cloud 2024.x (Dalston) jest zapewniona, ale aktualizacja wymaga przejścia na Jakarta EE 11. Koniecznie przetestuj circuit breakery i przepływy odpornościowe, ponieważ korzystają teraz z natywnych adnotacji @Retryable i @ConcurrencyLimit.