⌂ Index
Kapitel 04 · Web Reactive

Spring WebFlux, WebClient und Reactive Streams

Typ: FrameworkVersion 2 ausführlich

Spring WebFlux ist das reaktive Webframework für nicht-blockierende HTTP-Workloads, Streaming und Backpressure-orientierte Verarbeitung.

Fachliche Einordnung

WebFlux ist sinnvoll, wenn viele I/O-bound Operationen parallel laufen, etwa Gateway, Streaming, Chat, SSE oder reaktive Datenquellen. Für klassische JPA-Workloads ist MVC oft einfacher.

Enterprise-Merksatz: Spring WebFlux ist das reaktive Webframework für nicht-blockierende HTTP-Workloads, Streaming und Backpressure-orientierte Verarbeitung.

Technische Darstellung

Client Router/Controller Mono/Flux Pipeline WebClient Remote API Response Stream
Kernkonzepte
  • Reactor Mono und Flux als asynchrone Datenströme.
  • Netty oder Servlet 3.1+ als non-blocking Runtime.
  • WebClient als moderner HTTP-Client.
  • Backpressure und Operatoren wie map, flatMap, timeout und retryWhen.
  • Trennung zwischen reactive end-to-end und blockierenden Brücken.
Wann einsetzen?
  • Du brauchst hohe Nebenläufigkeit bei externen I/O-Aufrufen.
  • Du baust Streaming APIs oder Server-Sent Events.
  • Du nutzt R2DBC oder reaktive Treiber.
Typische Fehler und Risiken
  • Blocking JPA oder Thread.sleep in reaktiven Pipelines.
  • subscribe() im Anwendungscode statt Rückgabe von Mono/Flux.
  • Reactive nur wegen Trend nutzen, obwohl Team und Stack blockierend sind.
Legacy- und Modernisierungssicht

Alte SOAP/REST-Clients mit hoher Latenz können in einer WebClient-Fassade gekapselt werden; blockierende Adapter gehören dann auf boundedElastic oder in separate Services.

Ausführliches Beispiel

Das Beispiel zeigt bewusst nicht nur Annotationen, sondern auch die Verantwortung der Schicht. In echten Projekten sollte der technische Spring-Code an Adapter- oder Konfigurationsrändern bleiben, während die Fachlogik testbar und möglichst frameworkarm bleibt.

@Service
class CustomerCreditClient {
    private final WebClient webClient;

    CustomerCreditClient(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("https://credit.example.internal").build();
    }

    Mono<CreditDecision> check(CustomerId id) {
        return webClient.get()
            .uri("/credits/{id}", id.value())
            .retrieve()
            .bodyToMono(CreditDecision.class)
            .timeout(Duration.ofSeconds(2))
            .retryWhen(Retry.backoff(2, Duration.ofMillis(150)));
    }
}

@RestController
class CreditController {
    @GetMapping("/api/customers/{id}/credit")
    Mono<CreditDecision> credit(@PathVariable String id, CustomerCreditClient client) {
        return client.check(new CustomerId(id));
    }
}
Checkliste für Reviews
  • Ist die Verantwortung des Bausteins klar: Framework steuert Lebenszyklus, Library wird gezielt benutzt?
  • Ist die Fachlogik außerhalb von Controller, Listener, Repository-Implementierung oder Konfiguration?
  • Sind Fehlerfälle, Timeouts, Security, Monitoring und Tests sichtbar modelliert?
  • Gibt es klare Grenzen zwischen DTO, Domäne, Persistence und Infrastruktur?