Spring Boot 3.4: Alle Neuerungen im Detail

Spring Boot 3.4 bringt natives strukturiertes Logging, erweiterte Virtual Threads, standardmäßiges Graceful Shutdown und MockMvcTester. Vollständiger Leitfaden der neuen Funktionen.

Neue Funktionen und Verbesserungen in Spring Boot 3.4

Spring Boot 3.4, veröffentlicht im November 2024, liefert signifikante Verbesserungen für Entwicklerproduktivität und Anwendungsperformance. Diese Version führt natives strukturiertes Logging ein, aktiviert Graceful Shutdown standardmäßig und erweitert die Virtual-Thread-Unterstützung im gesamten Framework.

Voraussetzungen

Spring Boot 3.4 erfordert mindestens Java 17 und unterstützt Java 21 für Virtual Threads. Diese Version verwendet Spring Framework 6.2.

Natives strukturiertes Logging

Strukturiertes Logging stellt einen wesentlichen Fortschritt für die Anwendungsobservabilität dar. Statt textbasierter Logs, die schwer zu analysieren sind, erzeugt Spring Boot 3.4 JSON-Logs, die von Tools wie Elasticsearch, Grafana Loki oder Datadog konsumiert werden können.

Drei Formate werden nativ unterstützt: Elastic Common Schema (ECS), Logstash und 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

Diese einfache Konfiguration erzeugt automatisch formatierte strukturierte JSON-Logs.

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");
    }
}

Mit aktiviertem ECS-Format wird dieser Log zu einem JSON-Objekt mit Timestamp, Level, Nachricht, Klassenname, Thread und Anwendungsmetadaten. Diese Struktur vereinfacht die Suche und Aggregation in Monitoring-Plattformen.

Graceful Shutdown standardmäßig aktiviert

Wichtige Änderung: Graceful Shutdown ist jetzt standardmäßig aktiviert. Laufende HTTP-Anfragen werden vor dem Herunterfahren des Servers verarbeitet, wodurch 502-Fehler bei Deployments verhindert werden.

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

Dieses Verhalten gilt für alle eingebetteten Server: Tomcat, Jetty, Undertow und 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.
        };
    }
}

Diese Verbesserung vereinfacht die Implementierung von Zero-Downtime-Deployments ohne zusätzliche Konfiguration in den meisten Fällen.

Erweiterte Virtual-Thread-Unterstützung

Spring Boot 3.4 erweitert die Virtual-Thread-Unterstützung (Java 21+) auf weitere Komponenten. OtlpMeterRegistry und der Undertow-Server nutzen jetzt Virtual Threads, wenn diese aktiviert sind.

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

Diese einzelne Property transformiert das Threading-Modell der Anwendung. Jede HTTP-Anfrage erhält ihren eigenen Virtual Thread, was Tausende gleichzeitiger Verbindungen ermöglicht, ohne den Thread-Pool zu erschöpfen.

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);
    }
}

Virtual Threads glänzen bei I/O-lastigen Anwendungen: HTTP-Aufrufe, Datenbankabfragen, Dateioperationen. Der OS-Thread wird während der Wartezeit freigegeben und bei Wiederaufnahme neu zugewiesen.

Kompatibilität

Virtual Threads erfordern mindestens Java 21. Unter Java 17 wird diese Property ignoriert und das klassische Verhalten gilt.

Verbesserter RestClient und RestTemplate

Spring Boot 3.4 normalisiert die HTTP-Client-Konfiguration. Die Auswahl der HttpRequestFactory folgt jetzt einer klaren Rangfolge basierend auf dem 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();
    }
}

Auch die Konfiguration des Redirect-Verhaltens wird vereinfacht.

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

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

Für detailliertere Kontrolle ermöglicht der neue ClientHttpRequestFactoryBuilder eine vollständige programmatische Konfiguration.

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();
    }
}

Dieser Ansatz bietet maximale Flexibilität bei konsistenter API unabhängig von der zugrunde liegenden HTTP-Implementierung.

Bereit für deine Spring Boot-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

MockMvcTester für flüssiges Testing

Spring Boot 3.4 führt MockMvcTester ein, eine AssertJ-basierte Alternative zu MockMvc. Dieser neue Ansatz macht Tests lesbarer und ausdrucksstärker.

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");
    }
}

Im Vergleich zum bisherigen Ansatz mit MockMvc sind die Assertions prägnanter und die Verkettung wirkt natürlicher.

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 wird automatisch konfiguriert, wenn AssertJ im Classpath vorhanden ist (standardmäßig mit spring-boot-starter-test enthalten).

Verbessertes Docker Compose und Testcontainers

Die Docker-Compose-Unterstützung gewinnt an Flexibilität mit mehreren Konfigurationsdateien und benutzerdefinierten Argumenten.

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

Neue Services werden automatisch erkannt und konfiguriert.

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 erkennt diese Services automatisch und konfiguriert die entsprechenden Verbindungseigenschaften.

Für Tests mit Testcontainers werden neue Container unterstützt.

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");
    }
}

Die @ServiceConnection-Annotation konfiguriert Verbindungseigenschaften automatisch und eliminiert die Notwendigkeit von @DynamicPropertySource in den meisten Fällen.

Actuator SSL und Observabilität

Der neue /actuator/info-Endpoint stellt jetzt Informationen über konfigurierte SSL-Zertifikate bereit: Gültigkeitsdaten, Aussteller und Betreff.

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

Diese Funktion ermöglicht die Überwachung des Zertifikatsablaufs direkt über Actuator und erleichtert die Automatisierung von Erneuerungen.

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();
        };
    }
}

Für Observabilität unterstützt der OTLP-Transport jetzt gRPC zusätzlich zu 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

Die Anwendungsgruppe (spring.application.group) ermöglicht die logische Gruppierung mehrerer Services in Observability-Dashboards.

Leichtere OCI-Images

Der Standard-OCI-Image-Builder wechselt von paketobuildpacks/builder-jammy-base zu paketobuildpacks/builder-jammy-java-tiny und erzeugt deutlich kleinere Images.

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

    // New security flag
    trustBuilder = false

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

Der neue Parameter imagePlatform vereinfacht plattformübergreifende Builds für ARM und 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>

Diese optimierten Images starten schneller und verbrauchen weniger Ressourcen, besonders vorteilhaft für Kubernetes-Deployments.

Migrationshinweis

Der Builder-Wechsel kann Anwendungen betreffen, die von bestimmten System-Tools abhängen. Die neuen Images sollten vor dem Produktions-Deployment getestet werden.

Änderungen bei Bean Validation

Spring Boot 3.4 gleicht das Validierungsverhalten an die Bean-Validation-Spezifikation an. Die Validierung kaskadiert nicht mehr automatisch zu verschachtelten Properties.

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
}

Diese Änderung kann bestehende Anwendungen betreffen. @ConfigurationProperties-Klassen sollten geprüft und @Valid dort hinzugefügt werden, wo die Validierung kaskadieren soll.

Deprecation von @MockBean und @SpyBean

Die Spring-Boot-Annotationen @MockBean und @SpyBean sind zugunsten neuer Mockito-Annotationen deprecated.

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");
    }
}

Die alten Annotationen funktionieren weiterhin, zeigen aber Deprecation-Warnungen an. Die Migration zu @MockitoBean und @MockitoSpyBean sollte eingeplant werden.

Fang an zu üben!

Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.

Fazit

Spring Boot 3.4 liefert substanzielle Verbesserungen für Produktivität und Observabilität:

Migrations-Checkliste:

  • Strukturiertes Logging für verbesserte Observabilität aktivieren
  • Graceful-Shutdown-Verhalten prüfen (jetzt standardmäßig aktiviert)
  • Virtual Threads für I/O-lastige Anwendungen in Betracht ziehen (Java 21+)
  • Auf MockMvcTester für lesbarere Tests migrieren
  • @Valid bei verschachtelten @ConfigurationProperties-Properties ergänzen
  • @MockBean/@SpyBean durch @MockitoBean/@MockitoSpyBean ersetzen
  • Neue OCI-Images vor dem Produktions-Deployment testen

Spring Boot 3.4 festigt seine Position als Referenz-Framework für moderne Java-Entwicklung mit besonderem Augenmerk auf Observability-Standards und Performance.

Quellen:

Tags

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

Teilen

Verwandte Artikel