In the world of enterprise Java development, applications must be resilient, scalable, and efficient in the cloud. Spring Boot has been the standard for microservices for years, but with the rise of Kubernetes, GraalVM, and Java 21+, the demands have evolved.
Spring Boot 3 solidified simplicity with autoconfiguration, actuators, and support for reactive programming, facilitating microservices in Docker/Kubernetes. However, it had limitations: it required additional starters like Resilience4j, native builds were experimental, and observability depended on manual configurations. In real-world enterprise integration projects, this resulted in boilerplate and memory overhead. Multiprocessing with virtual threads helped, but native integration for concurrency limits and retries without external libraries was lacking. Strategies like manual circuit breakers worked, but they consumed resources and complicated the code, not being the definitive solution for high-availability cloud-native applications.
Spring Boot 4 introduces several key improvements, each with a direct impact on the robustness, performance, and maintainability of applications:
- Built-in resilience (@Retryable, @ConcurrencyLimit, @EnableResilientMethods): Enables handling transient failures (retries, concurrency limits) without adding external libraries, reducing complexity and improving service availability.
- JSpecify (@NonNull/@Nullable): Provides null safety at the compile level, preventing NullPointerExceptions in production and explicitly documenting the expectations of each API.
- Automatic API versioning: Facilitates exposing different versions of the same API without breaking existing clients, simplifying contract evolution.
- @HttpExchange (declarative HTTP client): Replaces repetitive RestTemplate/WebClient code with declarative interfaces, reducing boilerplate and making integration with external APIs more readable and maintainable.
- Enhanced modular autoconfiguration: Allows starting only what is necessary, reducing memory consumption and startup times in microservices.
- Micrometer 2.x + OpenTelemetry: Offers ready-to-use metrics and traces, improving observability without complex manual configuration.
- Jackson 3.0: Optimizes JSON serialization/deserialization, with improved performance and compatibility with the latest Java APIs.
- JUnit 6: Enhances the testing experience with new parameterization capabilities and extensions, facilitating more expressive and maintainable test suites.
- Improved Docker Buildpacks: Automatically generate lighter and optimized container images, simplifying the path to production in Docker/Kubernetes environments.
- Native images with GraalVM: Reduces memory usage and startup time (with typical savings of 30–50%), ideal for serverless and high-density microservices.
- Virtual threads by default in WebClient: Increases throughput in I/O-intensive applications by handling many concurrent requests with fewer resources.
- SpEL with Optional support: Enables safer and more expressive expressions, avoiding manual null checks.
- BeanRegistrar: Enables programmatic registration of beans at runtime, useful for dynamic scenarios or plugins.
- RestTestClient: Simplifies writing tests for REST endpoints, reducing auxiliary code and improving test readability.
- Null-safety integration in the container (via JSpecify): Adds consistency between null-safety annotations and the bean lifecycle.
- AOT compilation: Eliminates much of the reflection overhead, improving performance and being key for serverless and edge computing scenarios.
- Optimized free-threaded mode: Reduces the performance penalty in single-threaded applications and improves multi-core utilization in concurrent scenarios.
- Improved and color-coded error messages: Speed up problem diagnosis, shortening debugging time and the learning curve for new APIs.
Native resilience with @Retryable and @ConcurrencyLimit
Spring Boot 4 allows you to add resilience (retry and concurrency control) without external libraries, using only top-level annotations. Imagine a service that consumes an external API for product catalogs (or menus, users, etc.) that occasionally responds with 5xx errors or timeouts. In Spring Boot 3, the usual approach was to use Resilience4j or Spring Retry, add a starter, configure beans, and write fallback logic. In Spring Boot 4, with built-in resilience, you simply annotate the method.
The following minimum dependencies are added to pom.xml:
- spring-boot-starter-web
- spring-boot-starter-actuator (to view retry/concurrency metrics)
- Spring Boot version 4.x (e.g., 4.0.0)
Spring version in the pom
Necessary dependencies in the pom
Furthermore, for these annotations to work correctly, we need to add the following annotation to the application:
Note to activate features
Resilience configuration
With this annotation, Spring wraps the annotated methods with proxies that transparently handle retries, backoffs, concurrency limits, etc.
Method with implemented resilience
This method calls an endpoint implemented in the application to simulate potential failures of an external API.
Failure simulation method
Retryable automatically retries up to 3 times with exponential backoff for default exceptions (RuntimeException, timeouts, etc.).
Test
- Start the application and send several concurrent requests to /api/catalog/1.
- Observe:
The logs show how automatic retries are performed when the mock responds with an error. In the first request, the external API call is successfully received. In the second request for ID 12, it shows how it performs three retries until it successfully receives the information.
Request Logs
This demo illustrates how Spring Boot 4 integrates resilience patterns directly into the framework, simplifying a very common use case in microservices.
Declarative HTTP client with @HttpExchange and OpenTelemetry
This test demonstrates how to consume external APIs with a declarative client directly supported by Spring Boot 4 and Spring Framework 7. It also implements a complete native observability stack with OpenTelemetry: metrics (Prometheus), distributed traces (Tempo), and correlated logs (Loki), all visible in Grafana without additional code.
Previously, HTTP consumption in Spring was typically handled using:
- RestTemplate (imperative)
- WebClient (reactive)
Both involve manually constructing the call: URLs, headers, body conversion, error handling, etc. With `@HttpExchange`, you define an interface, and Spring automatically generates the client from annotations.
- `@HttpExchange` defines the base configuration for the interface (URL, format, etc.).
- `@GetExchange`, `@PostExchange`, and `@DeleteExchange` define the specific endpoints declaratively.
External API Service
Test
- Start the application and make requests to /api/todos:
- GET /api/todos
- GET /api/todos/{id}
- Observe that:
- There is no explicit RestTemplate or WebClient in the controller or the service.
- Any changes to the route, headers, or baseUrl are updated in a single point.
Observability
For testing, we set up a Docker Compose instance that runs five coordinated services forming a complete production observability system:
- otel-collector (ports 4318/9464): Single point of entry for all application OTLP telemetry. It receives metrics, traces, and logs, and redistributes them to specialized backends (Prometheus, Tempo, Loki).
- Prometheus (port 9090): Stores and queries numerical metrics such as HTTP latency, throughput, and JVM usage.
- Grafana (port 3000): Unified interface for visualizing dashboards. It automatically detects data sources and displays correlated metrics, traces, and logs.
- Loki (port 3100): Database of logs indexed by labels. It stores structured application logs with trace_ids for correlation.
tempo (port 3200): Stores distributed traces. Allows you to track the complete flow of a request from the HTTP input to the external API.
To configure our application, simply add this OpenTelemetry dependency to the pom.xml file, the standard for Spring Boot 4.
To configure our application, simply add this OpenTelemetry dependency to the pom.xml file, the standard for Spring Boot 4.
Native OpenTelemetry dependency
Then, to connect to the collector, all the telemetry is configured in the application.yml file:
Metrics, Traces, and Logs Configuration
Once configured, Grafana will allow us to view metrics through dashboards that it consumes from Prometheus, Tempo, and Loki.
Dashboard in Grafana
Dashboard Observability in Grafana
As you can see, the configuration is quite simple and saves us from having to add several more complex dependencies and extra configurations.
Conclusion
Spring Boot 4 represents a qualitative leap in Java development, transforming historical challenges into native strengths that boost productivity. Its seamless integration of resilience, zero-config observability, and AOT compilation eliminates unnecessary complexities, enabling fast and robust cloud-native applications with minimal configuration. This evolution positions Spring as a dominant framework for modern microservices and scalable architectures.
Patricio Flores
Software Technician
ALTIA