Spring Boot 3.4: Tutte le novità spiegate

Spring Boot 3.4 introduce logging strutturato nativo, virtual threads estesi, graceful shutdown predefinito e MockMvcTester. Guida completa alle nuove funzionalità.

Nuove funzionalità e miglioramenti di Spring Boot 3.4

Spring Boot 3.4, rilasciato a novembre 2024, offre miglioramenti significativi in termini di produttività e performance delle applicazioni. Questa versione introduce il logging strutturato nativo, abilita il graceful shutdown per impostazione predefinita ed estende il supporto ai virtual threads nell'intero framework.

Prerequisiti

Spring Boot 3.4 richiede Java 17 come minimo e supporta Java 21 per i virtual threads. Questa versione utilizza Spring Framework 6.2.

Logging strutturato nativo

Il logging strutturato rappresenta un avanzamento importante per l'osservabilità delle applicazioni. Invece di log testuali difficili da analizzare, Spring Boot 3.4 genera log in formato JSON consumabili da strumenti come Elasticsearch, Grafana Loki o Datadog.

Tre formati sono supportati nativamente: Elastic Common Schema (ECS), Logstash e 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

Questa semplice configurazione produce log JSON strutturati in modo automatico.

LoggingController.javajava
@RestController
@RequestMapping("/api/demo")
public class LoggingController {

    // Logger injection via SLF4J
    private static final Logger logger = LoggerFactory.getLogger(LoggingController.class);

    @GetMapping("/action")
    public ResponseEntity<String> 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 il formato ECS abilitato, questo log diventa un oggetto JSON contenente timestamp, livello, messaggio, nome della classe, thread e metadati dell'applicazione. Questa struttura semplifica la ricerca e l'aggregazione nelle piattaforme di monitoraggio.

Graceful Shutdown abilitato per impostazione predefinita

Cambiamento importante: il graceful shutdown è ora abilitato per impostazione predefinita. Le richieste HTTP in corso vengono elaborate prima dell'arresto del server, evitando errori 502 durante i deployment.

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

Questo comportamento si applica a tutti i server embedded: Tomcat, Jetty, Undertow e Reactor Netty.

LifecycleConfig.javajava
@Configuration
public class LifecycleConfig {

    private static final Logger logger = LoggerFactory.getLogger(LifecycleConfig.class);

    @Bean
    public ApplicationListener<ContextClosedEvent> 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.
        };
    }
}

Questo miglioramento semplifica l'implementazione di deployment senza downtime, senza necessità di configurazione aggiuntiva nella maggior parte dei casi.

Supporto esteso ai Virtual Threads

Spring Boot 3.4 estende il supporto ai virtual threads (Java 21+) a più componenti. OtlpMeterRegistry e il server Undertow ora utilizzano virtual threads quando abilitati.

properties
# application.properties
# Enable virtual threads globally
spring.threads.virtual.enabled=true

Questa singola proprietà trasforma il modello di threading dell'applicazione. Ogni richiesta HTTP ottiene il proprio virtual thread, consentendo migliaia di connessioni simultanee senza esaurire il pool di thread.

AsyncService.javajava
@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);
    }
}

I virtual threads eccellono nelle applicazioni con carico I/O elevato: chiamate HTTP, query al database, operazioni sui file. Il thread del sistema operativo viene rilasciato durante l'attesa e riassegnato alla ripresa.

Compatibilità

I virtual threads richiedono Java 21 come minimo. Su Java 17, questa proprietà viene ignorata e si applica il comportamento classico.

RestClient e RestTemplate migliorati

Spring Boot 3.4 normalizza la configurazione del client HTTP. La selezione dell'HttpRequestFactory segue ora una precedenza chiara basata sul classpath.

HttpClientConfig.javajava
@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();
    }
}

Anche la configurazione del comportamento dei redirect è semplificata.

properties
# application.properties
# Force specific implementation
spring.http.client.factory=jdk

# Configure redirect behavior
spring.http.client.redirects=dont-follow

Per un controllo più dettagliato, il nuovo ClientHttpRequestFactoryBuilder consente una configurazione programmatica completa.

CustomHttpClientConfig.javajava
@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();
    }
}

Questo approccio offre massima flessibilità mantenendo un'API coerente indipendentemente dall'implementazione HTTP sottostante.

Pronto a superare i tuoi colloqui su Spring Boot?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

MockMvcTester per test fluenti

Spring Boot 3.4 introduce MockMvcTester, un'alternativa basata su AssertJ a MockMvc. Questo nuovo approccio rende i test più leggibili ed espressivi.

UserControllerTest.javajava
@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");
    }
}

Rispetto all'approccio precedente con MockMvc, le asserzioni sono più concise e il concatenamento risulta più naturale.

ComparisonTest.javajava
@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 viene configurato automaticamente quando AssertJ è presente nel classpath (incluso di default con spring-boot-starter-test).

Docker Compose e Testcontainers migliorati

Il supporto Docker Compose guadagna flessibilità con file di configurazione multipli e argomenti personalizzati.

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

I nuovi servizi vengono rilevati e configurati automaticamente.

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 rileva automaticamente questi servizi e configura le proprietà di connessione corrispondenti.

Per i test con Testcontainers, nuovi container sono supportati.

IntegrationTestConfig.javajava
@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");
    }
}

L'annotazione @ServiceConnection configura automaticamente le proprietà di connessione, eliminando la necessità di @DynamicPropertySource nella maggior parte dei casi.

Actuator SSL e osservabilità

Il nuovo endpoint /actuator/info espone ora informazioni sui certificati SSL configurati: date di validità, emittente e soggetto.

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

Questa funzionalità consente il monitoraggio della scadenza dei certificati direttamente tramite actuator, facilitando l'automazione dei rinnovi.

SslMonitoringConfig.javajava
@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();
        };
    }
}

Per l'osservabilità, il trasporto OTLP ora supporta gRPC oltre a 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

Il gruppo applicazione (spring.application.group) consente di raggruppare logicamente più servizi nei dashboard di osservabilità.

Immagini OCI più leggere

Il builder predefinito per le immagini OCI passa da paketobuildpacks/builder-jammy-base a paketobuildpacks/builder-jammy-java-tiny, producendo immagini significativamente più piccole.

build.gradlegroovy
tasks.named("bootBuildImage") {
    // Native ARM support (new)
    imagePlatform = "linux/arm64"

    // New security flag
    trustBuilder = false

    // Image configuration
    imageName = "myregistry.com/myapp:${version}"
}

Il nuovo parametro imagePlatform semplifica le build multipiattaforma per ARM e x64.

xml
<!-- pom.xml -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <image>
            <!-- Native ARM support -->
            <platform>linux/arm64</platform>
            <!-- Builder optimized for minimal images -->
            <builder>paketobuildpacks/builder-jammy-java-tiny</builder>
        </image>
    </configuration>
</plugin>

Queste immagini ottimizzate si avviano più velocemente e consumano meno risorse, particolarmente vantaggioso per i deployment su Kubernetes.

Nota sulla migrazione

Il cambio di builder può influenzare applicazioni che dipendono da determinati strumenti di sistema. Si consiglia di testare le nuove immagini prima del deployment in produzione.

Modifiche alla Bean Validation

Spring Boot 3.4 allinea il comportamento della validazione alla specifica Bean Validation. La validazione non si propaga più automaticamente alle proprietà annidate.

ConfigProperties.javajava
@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
}

Questa modifica può influenzare le applicazioni esistenti. Si consiglia di verificare le classi @ConfigurationProperties e aggiungere @Valid dove la validazione deve propagarsi.

Deprecazione di @MockBean e @SpyBean

Le annotazioni @MockBean e @SpyBean di Spring Boot sono deprecate a favore delle nuove annotazioni Mockito.

UserServiceTest.java (new approach)java
@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");
    }
}

Le vecchie annotazioni funzionano ancora ma mostrano avvisi di deprecazione. Si consiglia di pianificare la migrazione a @MockitoBean e @MockitoSpyBean.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Conclusione

Spring Boot 3.4 offre miglioramenti sostanziali in produttività e osservabilità:

Checklist di migrazione:

  • Abilitare il logging strutturato per migliorare l'osservabilità
  • Verificare il comportamento del graceful shutdown (ora abilitato per impostazione predefinita)
  • Considerare i virtual threads per applicazioni con carico I/O elevato (Java 21+)
  • Migrare a MockMvcTester per test più leggibili
  • Aggiungere @Valid sulle proprietà @ConfigurationProperties annidate
  • Sostituire @MockBean/@SpyBean con @MockitoBean/@MockitoSpyBean
  • Testare le nuove immagini OCI prima del deployment in produzione

Spring Boot 3.4 consolida la sua posizione come framework di riferimento per lo sviluppo Java moderno, con particolare attenzione agli standard di osservabilità e performance.

Fonti:

Tag

#spring boot
#java
#spring boot 3.4
#spring framework
#backend

Condividi

Articoli correlati