Spring WebFlux Basics
Introduction
Spring WebFlux is the reactive web stack on Netty—non-blocking HTTP with Mono and Flux return types. Use it for high-concurrency IO or streaming endpoints. This chapter builds annotation-style controllers, compares WebFlux with Spring MVC, and handles errors in reactive APIs.
Prerequisites
Add WebFlux
Replace or add (avoid running MVC + WebFlux in same app without clear split):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>Remove spring-boot-starter-web if building pure WebFlux app—Boot picks one primary web stack.
Reactive Controller
Code explanation:
- Return
Mono<T>for single JSON body - Return
Flux<T>for array stream (JSON array when collected)
Service Layer
Replace in-memory store with R2DBC repository in production—chapter 27.
Functional Routing (Alternative)
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> itemRoutes(ItemHandler handler) {
return RouterFunctions.route()
.GET("/api/items", handler::list)
.GET("/api/items/{id}", handler::get)
.POST("/api/items", handler::create)
.build();
}
}Annotation style maps cleanly from MVC—functional style composes routes explicitly.
Global Error Handling
@RestControllerAdvice
public class ReactiveExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public Mono<ResponseEntity<ApiError>> handleBadRequest(IllegalArgumentException ex) {
return Mono.just(ResponseEntity.badRequest()
.body(new ApiError("BAD_REQUEST", ex.getMessage())));
}
public record ApiError(String code, String message) {}
}For WebExceptionHandler in pure functional apps—similar role to @RestControllerAdvice.
WebFlux vs Spring MVC
| Spring MVC | WebFlux | |
|---|---|---|
| Server | Tomcat/Jetty (Servlet) | Netty (default) |
| Thread model | Thread per request | Event loop + small pool |
| API style | Blocking | Mono/Flux |
| JDBC | Natural | Block on boundedElastic or use R2DBC |
| Ecosystem maturity | Widest | Growing |
Tip
Default to MVC Unless You Have a Measured Need
Team productivity and JDBC/JPA familiarity often outweigh theoretical concurrency gains.
Test With WebTestClient
FAQ
404 on same paths as MVC tutorial?
Different starter—confirm WebFlux on classpath and controller scanned.
Block JDBC in WebFlux?
Wrap with Mono.fromCallable(() -> jdbc).subscribeOn(Schedulers.boundedElastic())—anti-pattern at scale; prefer R2DBC.
SSE streaming?
produces = MediaType.TEXT_EVENT_STREAM_VALUE return Flux<ServerSentEvent>.
File upload reactive?
FilePart in multipart—different from MVC MultipartFile.
Spring Security reactive?
ServerHttpSecurity bean—chapter 27.
Port still 8080?
Yes—server.port same property.