actu-image
Software engineering - 29 paź 2025

Spring Boot 4: A Comprehensive Guide, from New Features to Full Migration

Vignette Image
Abdelaziz Louarit

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.

This guide provides a comprehensive and up-to-date analysis of Spring Boot 4:
  • 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);
    }
}
Figure 1 – Resilient service in Spring Boot 4 (Retries + Concurrency Limiting).

The @Retryable and @ConcurrencyLimit annotations allow you to:
  • 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
}
Figure 2 – Native API versioning: single controller, consistent endpoints.

Simply configure spring.mvc.versioning.strategy=header in your application.yml.


spring:
  mvc:
    versioning:
      strategy: header
Figure 3 – Spring configuration for header-based versioning.

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
        // ...
    }
}
Figure 4 – Explicit contract via nullability annotations.

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>
Figure 5 – Maven configuration for native build.

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
Figure 6 – Probes and SSL verification.

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) {}
Figure 7 – Jakarta DTO (Java record).

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>
Figure 8 – Spring Boot 4 RC BOM configuration.

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.