# Spring Boot 3.4 : Toutes les nouveautés et améliorations > Découvrez les nouveautés de Spring Boot 3.4 : structured logging, virtual threads, graceful shutdown par défaut, RestClient amélioré et 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, sorti en novembre 2024, apporte des améliorations majeures pour la productivité des développeurs et les performances des applications. Cette version introduit le structured logging natif, active le graceful shutdown par défaut, et étend le support des virtual threads. > **Prérequis** > > Spring Boot 3.4 nécessite Java 17 minimum et supporte Java 21 pour les virtual threads. Cette version utilise Spring Framework 6.2. ## Structured Logging natif Le structured logging représente une avancée majeure pour l'observabilité des applications. Au lieu de logs textuels difficiles à parser, Spring Boot 3.4 génère des logs au format JSON exploitables par des outils comme Elasticsearch, Grafana Loki ou Datadog. Trois formats sont supportés nativement : Elastic Common Schema (ECS), Logstash et Graylog Extended Log Format (GELF). ```properties # application.properties # Activer le structured logging en console logging.structured.format.console=ecs # Ou pour les fichiers de log logging.structured.format.file=logstash ``` Cette configuration simple produit des logs JSON structurés automatiquement. ```java // LoggingController.java @RestController @RequestMapping("/api/demo") public class LoggingController { // Injection du logger via SLF4J private static final Logger logger = LoggerFactory.getLogger(LoggingController.class); @GetMapping("/action") public ResponseEntity performAction(@RequestParam String userId) { // Le log sera automatiquement formaté en JSON structuré logger.info("Action performed by user: {}", userId); return ResponseEntity.ok("Action completed"); } } ``` Avec le format ECS activé, ce log devient un objet JSON contenant le timestamp, le niveau, le message, le nom de la classe, le thread et les métadonnées de l'application. Cette structure facilite les recherches et l'agrégation dans les plateformes de monitoring. ## Graceful Shutdown activé par défaut Changement important : le graceful shutdown est maintenant activé par défaut. Les requêtes HTTP en cours sont traitées avant l'arrêt du serveur, évitant les erreurs 502 lors des déploiements. ```properties # application.properties # Le graceful shutdown est maintenant ON par défaut # Pour restaurer l'ancien comportement (arrêt immédiat) : server.shutdown=immediate # Configurer le délai maximum d'attente (30s par défaut) spring.lifecycle.timeout-per-shutdown-phase=45s ``` Ce comportement s'applique à tous les serveurs embarqués : Tomcat, Jetty, Undertow et Reactor Netty. ```java // LifecycleConfig.java @Configuration public class LifecycleConfig { private static final Logger logger = LoggerFactory.getLogger(LifecycleConfig.class); @Bean public ApplicationListener gracefulShutdownListener() { // Ce listener s'exécute au début du shutdown return event -> { logger.info("Graceful shutdown initiated - completing in-flight requests"); // Logique personnalisée : fermer des connexions, sauvegarder l'état, etc. }; } } ``` Cette amélioration rend les déploiements zero-downtime plus simples à implémenter, sans configuration supplémentaire dans la plupart des cas. ## Virtual Threads étendus Spring Boot 3.4 étend le support des virtual threads (Java 21+) à davantage de composants. Le `OtlpMeterRegistry` et le serveur Undertow utilisent maintenant les virtual threads lorsqu'ils sont activés. ```properties # application.properties # Activer les virtual threads globalement spring.threads.virtual.enabled=true ``` Cette simple propriété transforme le modèle de threading de l'application. Chaque requête HTTP obtient son propre virtual thread, permettant de gérer des milliers de connexions simultanées sans épuiser le pool de threads. ```java // AsyncService.java @Service public class AsyncService { private static final Logger logger = LoggerFactory.getLogger(AsyncService.class); // Avec virtual threads activés, chaque appel bloquant // ne consomme plus de thread OS public String fetchExternalData() { logger.info("Executing on thread: {}", Thread.currentThread()); // Appel HTTP bloquant - avec virtual threads, // le thread OS est libéré pendant l'attente I/O return restClient.get() .uri("https://api.external.com/data") .retrieve() .body(String.class); } } ``` Les virtual threads brillent pour les applications I/O-bound : appels HTTP, requêtes base de données, opérations fichiers. Le thread OS est libéré pendant l'attente, puis réassigné à la reprise. > **Compatibilité** > > Les virtual threads nécessitent Java 21 minimum. Sur Java 17, cette propriété est ignorée et le comportement classique s'applique. ## RestClient et RestTemplate améliorés Spring Boot 3.4 normalise la configuration des clients HTTP. La sélection du `HttpRequestFactory` suit maintenant une priorité claire basée sur le classpath. ```java // HttpClientConfig.java @Configuration public class HttpClientConfig { @Bean public RestClient restClient(RestClient.Builder builder) { // Spring Boot choisit automatiquement l'implémentation : // 1. Apache HTTP Components (si présent) // 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(); } } ``` La configuration du comportement de redirection est également simplifiée. ```properties # application.properties # Forcer une implémentation spécifique spring.http.client.factory=jdk # Configurer le comportement des redirections spring.http.client.redirects=dont-follow ``` Pour un contrôle plus fin, le nouveau `ClientHttpRequestFactoryBuilder` permet une configuration programmatique complète. ```java // CustomHttpClientConfig.java @Configuration public class CustomHttpClientConfig { @Bean public RestClient customRestClient(ClientHttpRequestFactoryBuilder factoryBuilder) { // Configuration avancée du factory ClientHttpRequestFactory factory = factoryBuilder .httpComponents() .connectTimeout(Duration.ofSeconds(5)) .readTimeout(Duration.ofSeconds(30)) .build(); return RestClient.builder() .requestFactory(factory) .baseUrl("https://api.example.com") .build(); } } ``` Cette approche offre une flexibilité maximale tout en gardant une API cohérente, quelle que soit l'implémentation HTTP sous-jacente. ## MockMvcTester pour des tests fluides Spring Boot 3.4 introduit `MockMvcTester`, une alternative à `MockMvc` basée sur l'API fluide d'AssertJ. Cette nouvelle approche rend les tests plus lisibles et expressifs. ```java // UserControllerTest.java @WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvcTester mockMvc; // Nouvelle classe ! @Test void shouldReturnUserById() { // API fluide avec 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"); } } ``` Comparé à l'ancienne approche avec `MockMvc`, les assertions sont plus concises et le chaînage plus naturel. ```java // ComparisonTest.java @WebMvcTest(UserController.class) class ComparisonTest { @Autowired private MockMvcTester mockMvcTester; @Autowired private MockMvc mockMvc; @Test void withMockMvcTester() { // Nouvelle approche : fluide et 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 { // Ancienne approche : plus verbeuse mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content("{\"name\": \"Jane\"}")) .andExpect(status().isCreated()) .andExpect(header().string("Location", "/api/users/2")); } } ``` `MockMvcTester` est auto-configuré lorsque AssertJ est présent dans le classpath (inclus par défaut avec `spring-boot-starter-test`). ## Docker Compose et Testcontainers enrichis Le support Docker Compose gagne en flexibilité avec la possibilité d'utiliser plusieurs fichiers de configuration et de passer des arguments personnalisés. ```properties # application.properties # Utiliser plusieurs fichiers Docker Compose spring.docker.compose.file=compose.yaml,compose-dev.yaml # Passer des arguments au démarrage spring.docker.compose.start.arguments=--scale redis=2 # Arguments à l'arrêt spring.docker.compose.stop.arguments=--timeout 60 ``` De nouveaux services sont automatiquement détectés et configurés. ```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 détecte automatiquement ces services et configure les propriétés de connexion correspondantes. Pour les tests avec Testcontainers, de nouveaux conteneurs sont supportés. ```java // IntegrationTestConfig.java @TestConfiguration(proxyBeanMethods = false) public class IntegrationTestConfig { @Bean @ServiceConnection public PostgreSQLContainer postgresContainer() { return new PostgreSQLContainer<>("postgres:16"); } @Bean @ServiceConnection public RedisStackContainer redisStackContainer() { // Nouveau support Redis Stack return new RedisStackContainer("redis/redis-stack:latest"); } @Bean @ServiceConnection public LgtmStackContainer observabilityContainer() { // Nouveau support Grafana LGTM (Loki, Grafana, Tempo, Mimir) return new LgtmStackContainer("grafana/otel-lgtm:latest"); } } ``` L'annotation `@ServiceConnection` configure automatiquement les propriétés de connexion, éliminant le besoin de `@DynamicPropertySource` dans la plupart des cas. ## Actuator SSL et observabilité Le nouveau endpoint `/actuator/info` expose maintenant des informations sur les certificats SSL configurés : dates de validité, émetteur et sujet. ```properties # application.properties # Activer les informations SSL dans l'actuator management.info.ssl.enabled=true # Configurer un seuil d'alerte pour les certificats expirant bientôt management.health.ssl.certificate-validity-warning-threshold=30d ``` Cette fonctionnalité permet de monitorer l'expiration des certificats directement via l'actuator, facilitant l'automatisation des renouvellements. ```java // SslMonitoringConfig.java @Configuration public class SslMonitoringConfig { @Bean public HealthIndicator sslCertificateHealth(SslInfo sslInfo) { return () -> { // Vérification personnalisée de la validité des certificats 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(); }; } } ``` Pour l'observabilité, le transport OTLP supporte maintenant gRPC en plus de HTTP. ```properties # application.properties # Utiliser gRPC pour OTLP (traces et métriques) management.otlp.tracing.transport=grpc management.otlp.tracing.endpoint=http://localhost:4317 # Nouveau : grouper les applications spring.application.group=payment-services ``` Le groupe d'application (`spring.application.group`) permet de regrouper logiquement plusieurs services dans les dashboards d'observabilité. ## Images OCI plus légères Le builder par défaut pour les images OCI change de `paketobuildpacks/builder-jammy-base` vers `paketobuildpacks/builder-jammy-java-tiny`, produisant des images significativement plus petites. ```groovy // build.gradle tasks.named("bootBuildImage") { // Support ARM natif (nouveau) imagePlatform = "linux/arm64" // Nouveau flag de sécurité trustBuilder = false // Configuration de l'image imageName = "myregistry.com/myapp:${version}" } ``` Le nouveau paramètre `imagePlatform` facilite les builds cross-platform pour ARM et x64. ```xml org.springframework.boot spring-boot-maven-plugin linux/arm64 paketobuildpacks/builder-jammy-java-tiny ``` Ces images optimisées démarrent plus rapidement et consomment moins de ressources, particulièrement avantageux pour les déploiements Kubernetes. > **Migration** > > Le changement de builder peut affecter les applications dépendant de certains outils système. Testez la nouvelle image avant de déployer en production. ## Changements de validation des beans Spring Boot 3.4 aligne le comportement de validation sur la spécification Bean Validation. La validation ne cascade plus automatiquement vers les propriétés imbriquées. ```java // ConfigProperties.java @ConfigurationProperties(prefix = "app") @Validated public class AppConfig { @NotBlank private String name; // IMPORTANT : @Valid requis pour cascader la validation @Valid private DatabaseConfig database; // Sans @Valid, les contraintes de ServerConfig ne seront PAS vérifiées private ServerConfig server; // Getters et setters } public class DatabaseConfig { @NotBlank private String url; @Min(1) private int poolSize; // Getters et setters } public class ServerConfig { @NotNull // Cette contrainte ne sera PAS vérifiée sans @Valid sur le parent private Integer port; // Getters et setters } ``` Ce changement peut casser des applications existantes. Auditez les classes `@ConfigurationProperties` et ajoutez `@Valid` là où la validation doit cascader. ## Dépréciation de @MockBean et @SpyBean Les annotations `@MockBean` et `@SpyBean` de Spring Boot sont dépréciées au profit des nouvelles annotations Mockito. ```java // UserServiceTest.java (nouvelle approche) @SpringBootTest class UserServiceTest { // Nouveau : annotations Mockito natives @MockitoBean private UserRepository userRepository; @MockitoSpyBean private EmailService emailService; @Autowired private UserService userService; @Test void shouldCreateUser() { // Configuration du mock when(userRepository.save(any())).thenReturn(new User(1L, "test@example.com")); userService.createUser("test@example.com"); // Vérification du spy verify(emailService).sendWelcomeEmail("test@example.com"); } } ``` Les anciennes annotations fonctionnent encore mais affichent un avertissement de dépréciation. Planifiez la migration vers `@MockitoBean` et `@MockitoSpyBean`. ## Conclusion Spring Boot 3.4 apporte des améliorations substantielles pour la productivité et l'observabilité : **Checklist de migration :** - ✅ Activer le structured logging pour améliorer l'observabilité - ✅ Vérifier le comportement du graceful shutdown (maintenant activé par défaut) - ✅ Considérer les virtual threads pour les applications I/O-bound (Java 21+) - ✅ Migrer vers MockMvcTester pour des tests plus lisibles - ✅ Ajouter `@Valid` sur les propriétés imbriquées des `@ConfigurationProperties` - ✅ Remplacer `@MockBean`/`@SpyBean` par `@MockitoBean`/`@MockitoSpyBean` - ✅ Tester les nouvelles images OCI avant déploiement en production Spring Boot 3.4 consolide la position du framework comme référence pour le développement Java moderne, avec une attention particulière portée aux standards d'observabilité et aux performances. **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/fr/blog/spring-boot/spring-boot-3-4-nouveautes