# Spring Boot 3.4: All New Features Explained > Explore Spring Boot 3.4 new features: structured logging, virtual threads, graceful shutdown by default, improved RestClient and MockMvcTester. - Published: 2026-01-16 - Updated: 2026-03-31 - Author: SharpSkill - Tags: spring boot, java, spring boot 3.4, spring framework, backend - Reading time: 12 min --- Spring Boot 3.4, released in November 2024, delivers significant improvements for developer productivity and application performance. This version introduces native structured logging, enables graceful shutdown by default, and expands virtual thread support across the framework. > **Prerequisites** > > Spring Boot 3.4 requires Java 17 minimum and supports Java 21 for virtual threads. This version uses Spring Framework 6.2. ## Native Structured Logging Structured logging represents a major advancement for application observability. Instead of text-based logs that are difficult to parse, Spring Boot 3.4 generates JSON logs consumable by tools like Elasticsearch, Grafana Loki, or Datadog. Three formats are natively supported: Elastic Common Schema (ECS), Logstash, and Graylog Extended Log Format (GELF). ```properties # application.properties # Enable structured logging in console logging.structured.format.console=ecs # Or for log files logging.structured.format.file=logstash ``` This simple configuration produces automatically formatted structured JSON logs. ```java // LoggingController.java @RestController @RequestMapping("/api/demo") public class LoggingController { // Logger injection via SLF4J private static final Logger logger = LoggerFactory.getLogger(LoggingController.class); @GetMapping("/action") public ResponseEntity performAction(@RequestParam String userId) { // Log will be automatically formatted as structured JSON logger.info("Action performed by user: {}", userId); return ResponseEntity.ok("Action completed"); } } ``` With ECS format enabled, this log becomes a JSON object containing timestamp, level, message, class name, thread, and application metadata. This structure simplifies searching and aggregation in monitoring platforms. ## Graceful Shutdown Enabled by Default Important change: graceful shutdown is now enabled by default. In-flight HTTP requests are processed before server shutdown, preventing 502 errors during deployments. ```properties # application.properties # Graceful shutdown is now ON by default # To restore previous behavior (immediate shutdown): server.shutdown=immediate # Configure maximum wait timeout (30s default) spring.lifecycle.timeout-per-shutdown-phase=45s ``` This behavior applies to all embedded servers: Tomcat, Jetty, Undertow, and Reactor Netty. ```java // LifecycleConfig.java @Configuration public class LifecycleConfig { private static final Logger logger = LoggerFactory.getLogger(LifecycleConfig.class); @Bean public ApplicationListener gracefulShutdownListener() { // This listener executes at shutdown start return event -> { logger.info("Graceful shutdown initiated - completing in-flight requests"); // Custom logic: close connections, save state, etc. }; } } ``` This improvement makes zero-downtime deployments simpler to implement without additional configuration in most cases. ## Extended Virtual Threads Support Spring Boot 3.4 extends virtual thread support (Java 21+) to more components. `OtlpMeterRegistry` and the Undertow server now use virtual threads when enabled. ```properties # application.properties # Enable virtual threads globally spring.threads.virtual.enabled=true ``` This single property transforms the application's threading model. Each HTTP request gets its own virtual thread, enabling thousands of concurrent connections without exhausting the thread pool. ```java // AsyncService.java @Service public class AsyncService { private static final Logger logger = LoggerFactory.getLogger(AsyncService.class); // With virtual threads enabled, each blocking call // no longer consumes an OS thread public String fetchExternalData() { logger.info("Executing on thread: {}", Thread.currentThread()); // Blocking HTTP call - with virtual threads, // the OS thread is released during I/O wait return restClient.get() .uri("https://api.external.com/data") .retrieve() .body(String.class); } } ``` Virtual threads shine for I/O-bound applications: HTTP calls, database queries, file operations. The OS thread is released during wait time, then reassigned upon resumption. > **Compatibility** > > Virtual threads require Java 21 minimum. On Java 17, this property is ignored and classic behavior applies. ## Improved RestClient and RestTemplate Spring Boot 3.4 normalizes HTTP client configuration. `HttpRequestFactory` selection now follows a clear precedence based on classpath. ```java // HttpClientConfig.java @Configuration public class HttpClientConfig { @Bean public RestClient restClient(RestClient.Builder builder) { // Spring Boot automatically chooses implementation: // 1. Apache HTTP Components (if present) // 2. Jetty Client // 3. Reactor Netty // 4. JDK HttpClient (Java 11+) // 5. SimpleClientHttpRequestFactory (fallback) return builder .baseUrl("https://api.example.com") .defaultHeader("Accept", "application/json") .build(); } } ``` Redirect behavior configuration is also simplified. ```properties # application.properties # Force specific implementation spring.http.client.factory=jdk # Configure redirect behavior spring.http.client.redirects=dont-follow ``` For fine-grained control, the new `ClientHttpRequestFactoryBuilder` enables complete programmatic configuration. ```java // CustomHttpClientConfig.java @Configuration public class CustomHttpClientConfig { @Bean public RestClient customRestClient(ClientHttpRequestFactoryBuilder factoryBuilder) { // Advanced factory configuration ClientHttpRequestFactory factory = factoryBuilder .httpComponents() .connectTimeout(Duration.ofSeconds(5)) .readTimeout(Duration.ofSeconds(30)) .build(); return RestClient.builder() .requestFactory(factory) .baseUrl("https://api.example.com") .build(); } } ``` This approach offers maximum flexibility while maintaining a consistent API regardless of the underlying HTTP implementation. ## MockMvcTester for Fluent Testing Spring Boot 3.4 introduces `MockMvcTester`, an AssertJ-based alternative to `MockMvc`. This new approach makes tests more readable and expressive. ```java // UserControllerTest.java @WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvcTester mockMvc; // New class! @Test void shouldReturnUserById() { // Fluent API with AssertJ mockMvc.get().uri("/api/users/{id}", 1) .assertThat() .hasStatusOk() .hasContentType(MediaType.APPLICATION_JSON) .bodyJson() .extractingPath("$.name") .isEqualTo("John Doe"); } @Test void shouldReturn404ForUnknownUser() { mockMvc.get().uri("/api/users/{id}", 999) .assertThat() .hasStatus(HttpStatus.NOT_FOUND) .bodyJson() .extractingPath("$.error") .isEqualTo("User not found"); } } ``` Compared to the old approach with `MockMvc`, assertions are more concise and chaining feels more natural. ```java // ComparisonTest.java @WebMvcTest(UserController.class) class ComparisonTest { @Autowired private MockMvcTester mockMvcTester; @Autowired private MockMvc mockMvc; @Test void withMockMvcTester() { // New approach: fluent and concise mockMvcTester.post().uri("/api/users") .contentType(MediaType.APPLICATION_JSON) .content("{\"name\": \"Jane\"}") .assertThat() .hasStatus(HttpStatus.CREATED) .hasHeader("Location", "/api/users/2"); } @Test void withClassicMockMvc() throws Exception { // Old approach: more verbose mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content("{\"name\": \"Jane\"}")) .andExpect(status().isCreated()) .andExpect(header().string("Location", "/api/users/2")); } } ``` `MockMvcTester` is auto-configured when AssertJ is present on classpath (included by default with `spring-boot-starter-test`). ## Enhanced Docker Compose and Testcontainers Docker Compose support gains flexibility with multiple configuration files and custom arguments. ```properties # application.properties # Use multiple Docker Compose files spring.docker.compose.file=compose.yaml,compose-dev.yaml # Pass arguments at startup spring.docker.compose.start.arguments=--scale redis=2 # Arguments at shutdown spring.docker.compose.stop.arguments=--timeout 60 ``` New services are automatically detected and configured. ```yaml # compose.yaml services: postgres: image: postgres:16 environment: POSTGRES_DB: myapp POSTGRES_USER: user POSTGRES_PASSWORD: password ports: - "5432:5432" redis-stack: image: redis/redis-stack:latest ports: - "6379:6379" - "8001:8001" # RedisInsight UI grafana-lgtm: image: grafana/otel-lgtm:latest ports: - "3000:3000" # Grafana - "4317:4317" # OTLP gRPC ``` Spring Boot 3.4 automatically detects these services and configures corresponding connection properties. For testing with Testcontainers, new containers are supported. ```java // IntegrationTestConfig.java @TestConfiguration(proxyBeanMethods = false) public class IntegrationTestConfig { @Bean @ServiceConnection public PostgreSQLContainer postgresContainer() { return new PostgreSQLContainer<>("postgres:16"); } @Bean @ServiceConnection public RedisStackContainer redisStackContainer() { // New Redis Stack support return new RedisStackContainer("redis/redis-stack:latest"); } @Bean @ServiceConnection public LgtmStackContainer observabilityContainer() { // New Grafana LGTM support (Loki, Grafana, Tempo, Mimir) return new LgtmStackContainer("grafana/otel-lgtm:latest"); } } ``` The `@ServiceConnection` annotation automatically configures connection properties, eliminating the need for `@DynamicPropertySource` in most cases. ## Actuator SSL and Observability The new `/actuator/info` endpoint now exposes information about configured SSL certificates: validity dates, issuer, and subject. ```properties # application.properties # Enable SSL information in actuator management.info.ssl.enabled=true # Configure warning threshold for expiring certificates management.health.ssl.certificate-validity-warning-threshold=30d ``` This feature enables certificate expiration monitoring directly via actuator, facilitating renewal automation. ```java // SslMonitoringConfig.java @Configuration public class SslMonitoringConfig { @Bean public HealthIndicator sslCertificateHealth(SslInfo sslInfo) { return () -> { // Custom certificate validity check boolean allValid = sslInfo.getBundles().values().stream() .flatMap(bundle -> bundle.getCertificates().stream()) .allMatch(cert -> cert.getValidityEnds().isAfter(Instant.now())); return allValid ? Health.up().build() : Health.down().withDetail("reason", "Certificate expiring soon").build(); }; } } ``` For observability, OTLP transport now supports gRPC in addition to HTTP. ```properties # application.properties # Use gRPC for OTLP (traces and metrics) management.otlp.tracing.transport=grpc management.otlp.tracing.endpoint=http://localhost:4317 # New: group applications together spring.application.group=payment-services ``` Application group (`spring.application.group`) allows logical grouping of multiple services in observability dashboards. ## Lighter OCI Images The default OCI image builder changes from `paketobuildpacks/builder-jammy-base` to `paketobuildpacks/builder-jammy-java-tiny`, producing significantly smaller images. ```groovy // build.gradle tasks.named("bootBuildImage") { // Native ARM support (new) imagePlatform = "linux/arm64" // New security flag trustBuilder = false // Image configuration imageName = "myregistry.com/myapp:${version}" } ``` The new `imagePlatform` parameter simplifies cross-platform builds for ARM and x64. ```xml org.springframework.boot spring-boot-maven-plugin linux/arm64 paketobuildpacks/builder-jammy-java-tiny ``` These optimized images start faster and consume fewer resources, particularly beneficial for Kubernetes deployments. > **Migration Note** > > The builder change may affect applications depending on certain system tools. Test new images before deploying to production. ## Bean Validation Changes Spring Boot 3.4 aligns validation behavior with the Bean Validation specification. Validation no longer automatically cascades to nested properties. ```java // ConfigProperties.java @ConfigurationProperties(prefix = "app") @Validated public class AppConfig { @NotBlank private String name; // IMPORTANT: @Valid required to cascade validation @Valid private DatabaseConfig database; // Without @Valid, ServerConfig constraints will NOT be checked private ServerConfig server; // Getters and setters } public class DatabaseConfig { @NotBlank private String url; @Min(1) private int poolSize; // Getters and setters } public class ServerConfig { @NotNull // This constraint will NOT be checked without @Valid on parent private Integer port; // Getters and setters } ``` This change may break existing applications. Audit `@ConfigurationProperties` classes and add `@Valid` where validation should cascade. ## @MockBean and @SpyBean Deprecation Spring Boot's `@MockBean` and `@SpyBean` annotations are deprecated in favor of new Mockito annotations. ```java // UserServiceTest.java (new approach) @SpringBootTest class UserServiceTest { // New: native Mockito annotations @MockitoBean private UserRepository userRepository; @MockitoSpyBean private EmailService emailService; @Autowired private UserService userService; @Test void shouldCreateUser() { // Mock configuration when(userRepository.save(any())).thenReturn(new User(1L, "test@example.com")); userService.createUser("test@example.com"); // Spy verification verify(emailService).sendWelcomeEmail("test@example.com"); } } ``` Old annotations still work but display deprecation warnings. Plan migration to `@MockitoBean` and `@MockitoSpyBean`. ## Conclusion Spring Boot 3.4 delivers substantial improvements for productivity and observability: **Migration checklist:** - ✅ Enable structured logging for improved observability - ✅ Verify graceful shutdown behavior (now enabled by default) - ✅ Consider virtual threads for I/O-bound applications (Java 21+) - ✅ Migrate to MockMvcTester for more readable tests - ✅ Add `@Valid` on nested `@ConfigurationProperties` properties - ✅ Replace `@MockBean`/`@SpyBean` with `@MockitoBean`/`@MockitoSpyBean` - ✅ Test new OCI images before production deployment Spring Boot 3.4 consolidates the framework's position as the reference for modern Java development, with particular attention to observability standards and performance. **Sources:** - [Spring Boot 3.4.0 Release Announcement](https://spring.io/blog/2024/11/21/spring-boot-3-4-0-available-now/) - [Spring Boot 3.4 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.4-Release-Notes) - [Bootiful Spring Boot 3.4](https://spring.io/blog/2024/11/24/bootiful-34-boot/) --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/en/blog/spring-boot/spring-boot-3-4-new-features