Article written by
Abdelaziz LOUARIT,
Java Developer at MARGO.
Introduction
Spring Boot 4 marks a significant milestone for the modern Java ecosystem.
After several years of stability around Spring Boot 3, this new version pursues three core objectives: simplifying developer workflows, consolidating cloud-native best practices, and further accelerating startup times and observability.
Based on Spring Framework 7.x, it builds upon the advancements of previous versions while introducing native features dedicated to resilience, observability, and performance.
Aligned with Java 17 (LTS) as the new minimum requirement and Jakarta EE 11, Spring Boot 4 is clearly targeted at cloud-native and serverless applications. It features a notable reduction in external dependencies, built-in null-safety via JSpecify, and optimised support for AOT (Ahead-of-Time) compilation and GraalVM native images.
This version emphasises simplicity and consistency: fewer implicit configurations, clearer behaviours, and better readiness for containerised, fast-startup environments.
Compared to Spring Boot 3 (Java 17 / Jakarta EE 10), official benchmarks report up to a 50% improvement in startup time in native mode and a 30% to 40% reduction in memory consumption.
- Key new features and their practical implementation;
- A detailed comparison with Spring Boot 3;
- A step-by-step migration guide;
- An exhaustive migration checklist;
- And an expanded technical FAQ.
Production-Ready Features
Built-in Resilience: @Retryable and @ConcurrencyLimit
Previously managed via Spring Retry (or Resilience4j), resilience is now a native feature in Spring Boot 4. These annotations leverage Project Reactor for backoff strategies and integrate Micrometer for monitoring.
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);
}
}
- Automatically retry calls in the event of transient failures,
- Throttling/Rate limiting: Restrict the number of concurrent calls to a critical endpoint.
Native API Versioning
Versioning is now explicit within mappings, eliminating the need to duplicate controllers or create redundant paths. This reduces technical debt and clarifies the evolution of API contracts.
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
}
Simply configure spring.mvc.versioning.strategy=header in your application.yml.
spring:
mvc:
versioning:
strategy: header
Null‑safety (JSpecify)
Nullability annotations make contracts explicit: mandatory parameters, optional values, and nullable return types. IDEs leverage these to prevent NPEs (NullPointerExceptions) and guide auto-completion.
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
// ...
}
}
Performance: AOT & Native Builds (GraalVM)
Faster startup times and a reduced memory footprint have become first-class objectives. Spring Boot 4 strengthens AOT integration and simplifies GraalVM usage. AOT (Ahead-of-Time) compiles beans during build-time, reducing startup time by up to 90% in native mode. Enriched Maven Configuration:
<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>
Enriched Actuator (Readiness/Liveness, SSL, Mongo)
Health endpoints are more standard: readiness/liveness probes are enabled by default, and new indicators facilitate monitoring and Kubernetes integration.
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
probes:
enabled: true
show-details: always
ssl:
enabled: true
Jackson 3 & Jakarta EE 11
The transition to Jackson 3 and the full switch to `jakarta.*` modernise the ecosystem. Side effect: some third-party libraries require an update. Anticipate this early in your migration.
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Email;
public record CreateUserRequest(@NotBlank String name, @Email String email) {}
Migrating to Spring Boot 4
Quick Comparison: Spring Boot 3 → 4
| Feature | Spring Boot 3.x | Spring Boot 4.x |
|---|---|---|
| Minimum Java Version | 17 | 17 |
| JSON Binding | Jackson 2.15 | Jackson 3.0: Native support for records & improved serialisation performance |
| Resilience | Via external Spring Retry dependency | Built-in: @Retryable, @ConcurrencyLimit |
| Null-Safety | Optional via external libraries | Integrated (JSpecify) |
| EE Specs | Jakarta EE 10 | Jakarta EE 11 (Full migration) |
| AOT / GraalVM | Experimental / Unstable builds | Stabilised: 2x faster startup, –40% footprint |
| Actuator Probes | Manual configuration | Enabled by default (Kubernetes-ready) |
| Observability | Basic (Micrometer, Prometheus) | Full OpenTelemetry (OTel) integration: Traces, logs, metrics |
| Packaging / Build | Standard Docker image | Native GraalVM support and simplified Maven build tooling |
| Deprecations | Few (e.g., @ConstructorBinding) | Enriched metadata, improved IDE auto-completion |
| Update Frequency | Low | High |
Step-by-Step Migration Guide to Spring Boot 4
Based on the official guide: Migration Guide – Spring Boot 4.0
Phase 1 — Preparation
- Upgrade to Java 17 (or later) and Spring Boot 3.3.5; resolve any existing deprecations.
- Identify legacy dependencies: Run ./mvnw dependency:tree | grep javax to find any remaining javax.* dependencies.
- Refactor imports: Use your IDE to replace javax.validation with jakarta.validation, for example.
- Update critical components: Ensure drivers, starters, and core libraries are current.
- Establish a baseline: Measure current performance (startup time via time java -jar, memory consumption with JFR) to serve as a reference for post-migration comparisons.
Phase 2 — Milestone & RC Testing
- Dedicated Branch: Create a migration branch and switch the BOM to 4.0.0-M*.
- Compile & Fix: Update dependencies and resolve any Jakarta/Jackson breaking changes via your IDE.
- Progressive Updates: Refactor imports step-by-step, update starters, and run all tests.
- Clean up APIs: Identify and replace removed or deprecated APIs.
- Validation: Verify compatibility with Jackson 3, Spring Security, Hibernate, and JSON serialisation (specifically via unit and integration tests).
- AOT Metrics: Compare AOT metrics (startup, memory) against your baseline to detect regressions.
<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>
Phase 3 — Transition to General Availability (GA)
- Upgrade to 4.0.0 (GA).
- Prune dependencies: Remove obsolete libraries (e.g., Spring Retry if adopting native annotations).
- Endpoint Validation: Re-verify exposed endpoints (Actuator, Security).
- Deployment: Deploy to Staging, monitor with Micrometer/Prometheus, and proceed with a gradual production rollout.
Phase 4 — Optimisation
- Observability: Integrate Micrometer to track retries, successes, and errors (e.g., counters on @Retryable); export to Prometheus.
- Final Benchmark: Compare Before vs. After results. Measure gains (startup, memory via JFR/JMeter) against the initial baseline and calculate percentage deltas to validate impact.
- Knowledge Sharing: Document and share lessons learned with the team.
Pre-Production Migration Checklist
- Java 17 (or later) deployed across all environments
- Zero remaining
javax.*dependencies - Libraries updated for Jackson 3 (HATEOAS, MapStruct, Lombok, Hibernate, etc.)
- YAML properties reviewed for any potential renames
- Native resilience enabled / Spring Retry removed
- Actuator verified (including active probes)
- Integration and load tests successfully completed
- AOT/GraalVM tested if targeting native deployment
Glossary of Terms
- AOT (Ahead-Of-Time): Pre-compiling Java code before execution to reduce startup time and memory consumption.
- GraalVM: An optimised virtual machine that enables the creation of native executables and supports polyglot execution (Java, Python, etc.).
- OTel (OpenTelemetry): An open standard for collecting, tracing, and centralising application metrics, logs, and traces.
Conclusion
Spring Boot 4 solidifies the Java ecosystem for the cloud-native era: native resilience, advanced observability via OpenTelemetry (OTel), and AOT/GraalVM optimisations that drastically reduce the system’s footprint (up to -40% memory in native mode, according to official benchmarks). By migrating step-by-step—from preparation in 3.3.x to fine-tuning—you minimise risks while adopting modern standards such as Java 21 (and its Virtual Threads), Jakarta EE 11, Jackson 3, and unified observability.
Ultimately, this version transforms your applications into more predictable, scalable, and maintainable systems, with fewer external dependencies and a sharper focus on business logic. The efforts yield clear rewards:
- Increased robustness against transient failures (retry/backoff, concurrency limits),
- Unified monitoring (traces, metrics, logs via OTel) and standardised health checks,
- Serverless-ready performance (faster startups, reduced footprint, native images).
Spring Boot 4 is not a disruption; it is a strategic upgrade: a modern foundation for sustainable, fast, and observable Java applications—fully equipped for the multi-cloud demands of 2025 (and beyond).
To further expand your Java knowledge and stay up to date, read our article Java 25 – Key Takeaways.
Should we migrate all services at once?
No. We recommend an incremental approach based on business domains (using the Strangler Pattern). Prioritise critical services or new modules to validate the performance gains offered by AOT and GraalVM.
Can we remain on version 3.x?
Spring Boot 3.3.x reaches its end of OSS support in June 2025, and Spring Boot 3.5.x is supported until 30 June 2026. You should anticipate the transition to ensure:
- Jackson 3 compatibility, as it becomes the new default,
- Jakarta EE 11 readiness for future compatibility,
- and Security, as patches will cease once the LTS support window closes.
Is AOT/GraalVM mandatory?
No. Target workloads where the ROI is proven: serverless, edge computing, or microservices with frequent restarts.
Does Null-Safety require heavy refactoring?
No. Start with your most critical modules and adopt it incrementally.
What is the impact on Spring Cloud?
Compatibility is ensured with Spring Cloud 2024.x (Dalston), but the upgrade requires moving to Jakarta EE 11. Be sure to test your circuit breakers and resilience flows, as they now leverage the native @Retryable and @ConcurrencyLimit annotations.