Using WebFlux with Spring Boot is straightforward and may not require changes when moving from Spring MVC. We will still use the same set of annotations like RestController and RequestMapping, to name a few. However, we use WebFlux to tap the non-blocking I/O operations for better performance. Therefore, we still need to change the codes we developed for Spring MVC to follow the reactive programming paradigm to take advantage of the benefits.
Spring MVC And Spring WebFlux – What is the difference?
In a nutshell, Spring MVC works with thread-per-request operations, which are synchronous. While Spring WebFlux uses an event loop and allows for non-blocking and asynchronous operations in Spring Boot.
Although Spring Boot now has the async annotation – @Async – to make an operation asynchronous, e.g., HTTP GET, it still uses a thread for each incoming request.
When Using Them
Spring MVC and WebFlux are mutually exclusive. Meaning, we cannot use both Spring MVC and WebFlux with Spring Boot simultaneously. We can use either dependency in our pom.xml.
When we use Spring WebFlux with Spring Boot, we will have the following dependency.
1 2 3 4 5 6 7 | ... <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> ... |
On the other hand, we use the following dependency with Spring Boot when using Spring MVC.
1 2 3 4 5 6 | ... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ... |
Asynchronous Operations With Spring WebFlux
To turn some public methods in a controller to asynchronous, we need to return values wrapped in either Mono or Flux. We use Mono when we return single values and Flux when we produce a collection of values. Consider the following examples.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package com.turreta.blog.springbootwebflux; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.List; @RestController @RequestMapping("/demo") public class MyController { @Autowired private PersonService personService; @GetMapping("/{personId}") @ResponseBody public Mono<PersonDTO> findPerson(@PathVariable String personId) { PersonDTO personDTO = personService.findById(personId); return Mono.just(personDTO); } @GetMapping @ResponseBody public Flux<PersonDTO> getAll() { // Return 1000000 objects List<PersonDTO> personDTOs = personService.getAll(); return Flux.fromIterable(personDTOs); } } |
These endpoints are now asynchronous. For example, when we want to retrieve a single Person’s data, we use HTTP GET /demo/1. On the other hand, when we want to get all Person data, we use HTTP GET /demo.
When we return data to client applications, we need to convert them using static methods available in the Mono and Flux classes. For instance, to produce a collection of PersonDTO as Flux, we use the fromIterable method.