# Spring Boot 3.4: Todas las novedades explicadas > Spring Boot 3.4 trae logging estructurado nativo, virtual threads extendidos, graceful shutdown por defecto y MockMvcTester. Guía completa de las nuevas funcionalidades. - Published: 2026-01-16 - Updated: 2026-04-10 - Author: SharpSkill - Tags: spring boot, java, spring boot 3.4, spring framework, backend - Reading time: 12 min --- Spring Boot 3.4, lanzado en noviembre de 2024, aporta mejoras significativas en productividad y rendimiento de aplicaciones. Esta versión introduce logging estructurado nativo, habilita graceful shutdown por defecto y amplía el soporte de virtual threads en todo el framework. > **Requisitos previos** > > Spring Boot 3.4 requiere Java 17 como mínimo y soporta Java 21 para virtual threads. Esta versión utiliza Spring Framework 6.2. ## Logging estructurado nativo El logging estructurado representa un avance importante para la observabilidad de aplicaciones. En lugar de logs basados en texto difíciles de analizar, Spring Boot 3.4 genera logs en formato JSON consumibles por herramientas como Elasticsearch, Grafana Loki o Datadog. Tres formatos son soportados de forma nativa: Elastic Common Schema (ECS), Logstash y 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 ``` Esta simple configuración produce logs JSON estructurados de forma automática. ```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"); } } ``` Con el formato ECS habilitado, este log se convierte en un objeto JSON que contiene timestamp, nivel, mensaje, nombre de clase, hilo y metadatos de la aplicación. Esta estructura simplifica la búsqueda y agregación en plataformas de monitoreo. ## Graceful Shutdown habilitado por defecto Cambio importante: el graceful shutdown está ahora habilitado por defecto. Las peticiones HTTP en curso se procesan antes del cierre del servidor, evitando errores 502 durante los despliegues. ```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 ``` Este comportamiento aplica a todos los servidores embebidos: Tomcat, Jetty, Undertow y 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. }; } } ``` Esta mejora simplifica la implementación de despliegues sin tiempo de inactividad, sin necesidad de configuración adicional en la mayoría de los casos. ## Soporte extendido de Virtual Threads Spring Boot 3.4 extiende el soporte de virtual threads (Java 21+) a más componentes. `OtlpMeterRegistry` y el servidor Undertow ahora utilizan virtual threads cuando están habilitados. ```properties # application.properties # Enable virtual threads globally spring.threads.virtual.enabled=true ``` Esta única propiedad transforma el modelo de hilos de la aplicación. Cada petición HTTP obtiene su propio virtual thread, permitiendo miles de conexiones concurrentes sin agotar el pool de hilos. ```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); } } ``` Los virtual threads destacan en aplicaciones con alta carga de I/O: llamadas HTTP, consultas a bases de datos, operaciones con archivos. El hilo del sistema operativo se libera durante el tiempo de espera y se reasigna al reanudar. > **Compatibilidad** > > Los virtual threads requieren Java 21 como mínimo. En Java 17, esta propiedad se ignora y se aplica el comportamiento clásico. ## RestClient y RestTemplate mejorados Spring Boot 3.4 normaliza la configuración del cliente HTTP. La selección de `HttpRequestFactory` ahora sigue una precedencia clara basada en el 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(); } } ``` La configuración del comportamiento de redirecciones también se simplifica. ```properties # application.properties # Force specific implementation spring.http.client.factory=jdk # Configure redirect behavior spring.http.client.redirects=dont-follow ``` Para un control más detallado, el nuevo `ClientHttpRequestFactoryBuilder` permite una configuración programática completa. ```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(); } } ``` Este enfoque ofrece máxima flexibilidad manteniendo una API consistente independientemente de la implementación HTTP subyacente. ## MockMvcTester para testing fluido Spring Boot 3.4 introduce `MockMvcTester`, una alternativa basada en AssertJ a `MockMvc`. Este nuevo enfoque hace que los tests sean más legibles y expresivos. ```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"); } } ``` Comparado con el enfoque anterior usando `MockMvc`, las aserciones son más concisas y el encadenamiento resulta más 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` se configura automáticamente cuando AssertJ está presente en el classpath (incluido por defecto con `spring-boot-starter-test`). ## Docker Compose y Testcontainers mejorados El soporte de Docker Compose gana flexibilidad con múltiples archivos de configuración y argumentos personalizados. ```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 ``` Nuevos servicios se detectan y configuran automáticamente. ```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 detecta automáticamente estos servicios y configura las propiedades de conexión correspondientes. Para testing con Testcontainers, nuevos contenedores son soportados. ```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"); } } ``` La anotación `@ServiceConnection` configura automáticamente las propiedades de conexión, eliminando la necesidad de `@DynamicPropertySource` en la mayoría de los casos. ## Actuator SSL y observabilidad El nuevo endpoint `/actuator/info` ahora expone información sobre los certificados SSL configurados: fechas de validez, emisor y sujeto. ```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 ``` Esta funcionalidad permite monitorear la expiración de certificados directamente via actuator, facilitando la automatización de renovaciones. ```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(); }; } } ``` Para observabilidad, el transporte OTLP ahora soporta gRPC además de 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 ``` El grupo de aplicación (`spring.application.group`) permite agrupar lógicamente múltiples servicios en dashboards de observabilidad. ## Imágenes OCI más ligeras El builder de imágenes OCI por defecto cambia de `paketobuildpacks/builder-jammy-base` a `paketobuildpacks/builder-jammy-java-tiny`, produciendo imágenes significativamente más pequeñas. ```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}" } ``` El nuevo parámetro `imagePlatform` simplifica las builds multiplataforma para ARM y x64. ```xml org.springframework.boot spring-boot-maven-plugin linux/arm64 paketobuildpacks/builder-jammy-java-tiny ``` Estas imágenes optimizadas arrancan más rápido y consumen menos recursos, especialmente beneficioso para despliegues en Kubernetes. > **Nota de migración** > > El cambio de builder puede afectar a aplicaciones que dependan de ciertas herramientas del sistema. Es recomendable probar las nuevas imágenes antes de desplegar en producción. ## Cambios en Bean Validation Spring Boot 3.4 alinea el comportamiento de validación con la especificación de Bean Validation. La validación ya no se propaga automáticamente a propiedades anidadas. ```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 } ``` Este cambio puede afectar aplicaciones existentes. Se recomienda auditar las clases `@ConfigurationProperties` y agregar `@Valid` donde la validación debe propagarse. ## Deprecación de @MockBean y @SpyBean Las anotaciones `@MockBean` y `@SpyBean` de Spring Boot están deprecadas en favor de las nuevas anotaciones de Mockito. ```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"); } } ``` Las anotaciones antiguas siguen funcionando pero muestran advertencias de deprecación. Se recomienda planificar la migración a `@MockitoBean` y `@MockitoSpyBean`. ## Conclusión Spring Boot 3.4 ofrece mejoras sustanciales en productividad y observabilidad: **Checklist de migración:** - Habilitar logging estructurado para mejorar la observabilidad - Verificar el comportamiento de graceful shutdown (ahora habilitado por defecto) - Considerar virtual threads para aplicaciones con alta carga de I/O (Java 21+) - Migrar a MockMvcTester para tests más legibles - Agregar `@Valid` en propiedades `@ConfigurationProperties` anidadas - Reemplazar `@MockBean`/`@SpyBean` por `@MockitoBean`/`@MockitoSpyBean` - Probar las nuevas imágenes OCI antes del despliegue en producción Spring Boot 3.4 consolida su posición como el framework de referencia para el desarrollo Java moderno, con especial atención a los estándares de observabilidad y rendimiento. **Fuentes:** - [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/es/blog/spring-boot/spring-boot-3-4-new-features