Building Reactive Applications with WebFlux
1. Adding Spring WebFlux Dependencies
Example: WebFlux dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Warning: Don't include
spring-boot-starter-web together — Boot
picks the wrong server type.2. Understanding Reactive Streams
| Component | Role |
|---|---|
Publisher<T> |
Emits items asynchronously |
Subscriber<T> |
Consumes items + signals |
Subscription |
Backpressure control: request(n), cancel() |
Processor |
Both publisher + subscriber |
3. Using Mono for Single Values
Example: Mono: map and default value
Mono<User> user = userRepo.findById(id);
Mono<String> name = user.map(User::getName).defaultIfEmpty("anonymous");
Mono.empty(); Mono.just(x); Mono.error(ex);
4. Using Flux for Multiple Values
Example: Flux: filter and limit stream
Flux<Order> recent = orderRepo.findByCreatedAfter(Instant.now().minus(1, HOURS))
.filter(o -> o.total().compareTo(BigDecimal.TEN) > 0)
.take(100);
5. Creating Reactive REST Controllers
Example: Reactive CRUD controller
@RestController
@RequestMapping("/api/users")
public class UserApi {
private final UserRepo repo;
public UserApi(UserRepo repo) { this.repo = repo; }
@GetMapping public Flux<User> all() { return repo.findAll(); }
@GetMapping("/{id}") public Mono<ResponseEntity<User>> one(@PathVariable Long id) {
return repo.findById(id).map(ResponseEntity::ok).defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping public Mono<User> create(@RequestBody Mono<User> body) {
return body.flatMap(repo::save);
}
}
6. Implementing Reactive Repositories
| Interface | For |
|---|---|
ReactiveCrudRepository |
Generic reactive CRUD |
ReactiveSortingRepository |
+ Sort |
R2dbcRepository |
R2DBC SQL DBs |
ReactiveMongoRepository |
MongoDB |
ReactiveRedisRepository |
Redis |
7. Using Reactive Database Drivers (R2DBC)
Example: R2DBC datasource configuration
spring:
r2dbc:
url: r2dbc:postgresql://localhost/app
username: app
password: ${DB_PASS}
8. Handling Backpressure Strategies
| Operator | Strategy |
|---|---|
onBackpressureBuffer() |
Buffer overflow |
onBackpressureDrop() |
Drop excess items |
onBackpressureLatest() |
Keep latest only |
onBackpressureError() |
Signal error |
limitRate(n) |
Cap upstream demand |
9. Implementing Server-Sent Events
Example: Server-sent events stream with Flux
@GetMapping(value="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Quote>> quotes() {
return Flux.interval(Duration.ofSeconds(1))
.map(i -> ServerSentEvent.<Quote>builder()
.id(String.valueOf(i)).event("quote").data(quoteService.next()).build());
}
10. Testing Reactive Endpoints (WebTestClient)
Example: Test reactive endpoint with WebTestClient
@WebFluxTest(UserApi.class)
class UserApiTests {
@Autowired WebTestClient client;
@MockBean UserRepo repo;
@Test void list() {
when(repo.findAll()).thenReturn(Flux.just(new User(1L,"a")));
client.get().uri("/api/users").exchange()
.expectStatus().isOk().expectBodyList(User.class).hasSize(1);
}
}
11. Using Reactive Operators
| Operator | Purpose |
|---|---|
map / flatMap |
Transform / chain async |
filter |
Predicate |
zip / merge / concat |
Combine |
switchIfEmpty / defaultIfEmpty |
Empty fallback |
retry / retryWhen |
Re-subscribe on error |
timeout(d) |
Error if no signal |
publishOn / subscribeOn |
Switch scheduler |
cache() |
Replay to late subscribers |