Ein Reactive REST Client in Spring WebFlux ist ein HTTP-Client, der nicht blockierend arbeitet.
Das bedeutet:
- Der Thread wartet nicht auf die Antwort.
- Während die Antwort kommt, kann der Thread andere Requests bearbeiten.
- Gut für:
- viele parallele Requests
- Microservices
- Streaming
- hohe Skalierung
In Spring Boot WebFlux verwendet man meistens:
- WebClient statt RestTemplate
RestTemplate = klassisch/blockierend
WebClient = reactive/non-blocking
Grundkonzept einfach erklärt
Klassisch/blockierend
String result = restTemplate.getForObject(url, String.class);
Der Thread wartet hier: Bei vielen Requests braucht man viele Threads.
Reactive/non-blocking
Mono<String> result = webClient.get().uri(„/users“).retrieve()
.bodyToMono(String.class);
Hier wartet der Thread nicht aktiv.
Stattdessen:
Request senden, Thread freigeben, Antwort kommt später und Weiterverarbeitung starten
Wichtige Reactive Typen
Mono
0 oder 1 Ergebnis.
Mono<User>
Flux
0 bis N Ergebnisse.
Flux<User>
Minimales Beispiel
Maven Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Einfaches DTO
public class Todo {
private Long id;
private String title;
public Todo() {
}
public Todo(Long id, String title) {
this.id = id;
this.title = title;
}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setId(Long id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
}
Reactive REST Client
Service mit WebClient
@Service
public class TodoClient {
private final WebClient webClient;
public TodoClient(WebClient.Builder builder) {
this.webClient = builder
.baseUrl(„https://example.com“)
.build();
}
public Mono<Todo> getTodo(Long id) {
return webClient.get()
.uri(„/todos/{id}“, id)
.retrieve()
.onStatus(
status -> status.is4xxClientError(),
response -> Mono.error(
new RuntimeException(„4xx Fehler“))
)
.onStatus(
status -> status.is5xxServerError(),
response -> Mono.error(
new RuntimeException(„5xx Fehler“))
)
.bodyToMono(Todo.class)
.timeout(Duration.ofSeconds(3))
.doOnSuccess(todo ->
System.out.println(„Success: “ + todo.getTitle()))
.doOnError(error ->
System.out.println(„Error: “ + error.getMessage()))
.onErrorResume(error -> {
System.out.println(„Fallback aktiviert“);
return Mono.just(
new Todo(-1L, „Fallback Todo“));
});
}
}
Controller Beispiel
@RestController
@RequestMapping(„/api“)
public class TodoController {
private final TodoClient todoClient;
public TodoController(TodoClient todoClient) {
this.todoClient = todoClient;
}
@GetMapping(„/todo/{id}“)
public Mono<Todo> getTodo(@PathVariable Long id) {
return todoClient.getTodo(id);
}
}
Warum gibt Controller ein Mono zurück?
Weil WebFlux komplett reactive arbeitet.
Spring verarbeitet das asynchron.
Minimaler Test
Hier testet man den Client.
Test Dependency
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
Einfacher Test
@SpringBootTest
class TodoClientTest {
@Autowired
private TodoClient todoClient;
@Test
void shouldReturnTodo() {
Mono<Todo> mono = todoClient.getTodo(1L);
StepVerifier.create(mono)
.expectNextMatches(todo ->
todo.getId() != null)
.verifyComplete();
}
}
Klassisch
Mach Request
Warte
Verarbeite Antwort
Reactive
Definiere Pipeline
Wenn Antwort kommt:
→ verarbeite Daten
→ handle Fehler
→ sende weiter
Man beschreibt eher einen Datenfluss.
Wichtige Operatoren
map
Objekt transformieren.
mono.map(todo -> todo.getTitle())
flatMap
Asynchrone Verkettung.
mono.flatMap(todo -> anotherCall())
filter
.filter(todo -> todo.getId() > 0)
onErrorResume
Fallback.
Wenn kein Thread wartet:
Wie kommt die Antwort später zurück?
Event Loop + Callback Mechanismus + Betriebssystem Netzwerk-Events sorgt dafür dass die Antwort vervollständigt wird.
In Spring WebFlux passiert im Hintergrund sehr viel automatisch.
Klassisches Blocking nochmal
Normaler RestTemplate
String result = restTemplate.getForObject(…);
Intern:
Thread sendet HTTP Request
Thread wartet/blockiert
Socket wartet
Antwort kommt
Thread läuft weiter
Der Thread ist die ganze Zeit beschäftigt.
Reactive Ansatz
Mit WebClient:
Mono<Todo> mono = webClient.get()…
Hier passiert etwas komplett anderes.
Reactive Modell
Thread:
Request senden
„Informiere mich später“
Thread frei
(Event Loop überwacht Netzwerk)
Antwort kommt
Event ausgelöst
Pipeline weiter
Warum ist das skalierbarer?
Weil Threads teuer sind.
Ein blockierter Thread braucht:
- RAM
- Context Switches
- CPU Verwaltung
Mit Reactive:
Wenige Threads
Viele Requests
Beispiel:
| Modell | Requests |
| Blocking | 1000 Threads für 1000 langsame Requests |
| Reactive | vielleicht 8-20 Threads |
Wichtiger Denkwechsel
Reactive bedeutet:
Nicht:
„warte auf Ergebnis“
Sondern:
„registriere was passieren soll,
wenn Ergebnis kommt“
Warum Mono zuerst „nichts enthält“
Das ist extrem wichtig.
Mono<Todo>
ist NICHT das Ergebnis selbst.
Es ist eher:
Ein Versprechen auf zukünftige Daten
Ähnlich wie:
- Future
- Promise in JavaScript
Beispiel
Mono<Todo> mono = webClient.get()…
Hier existiert das Todo vielleicht noch gar nicht.
Du definierst nur:
Was passieren soll,
wenn es irgendwann kommt.
map() passiert später
mono.map(todo -> todo.getTitle())
Nicht sofort.
Sondern erst:
wenn Antwort angekommen ist
Subscriber startet alles
Sehr wichtig:
Reactive Streams sind oft „lazy“.
Erst ein Subscriber startet die Pipeline.
Zum Beispiel:
mono.subscribe(…)
Oder Spring WebFlux subscribt automatisch im Controller.
Warum flatMap wichtig ist
Wenn weiterer async Call kommt:
getUser()
.flatMap(user -> getOrders(user))
Bedeutet:
Wenn User da:
starte neuen async Call

Hinterlasse einen Kommentar