Reactive Programming with Spring 5 WebFlux

Introduction

Spring WebFlux is Spring's response to the rising issue of blocking I/O architecture.

As data is becoming more and more crucial in our age, the approaches we take to retrieve and manipulate it changes. Conventionally, most approaches were "blocking", or rather, synchronous. What this means is that accessing a resource blocked the application from accessing/processing another one until the previous resource was dealt with.

This was perfectly fine with a limited amount of data and resources, though with rising demand for data through high-performance applications - this became a huge problem.

Publishers started overwhelming subscribers and handling resources one-by-one, just like having a single clerk work in the whole supermarket, became too slow for a smooth user experience.

The solution is obvious - have more clerks handle the customers. In terms of software applications, this means a multi-threaded environment and asynchronous, non-blocking calls.

Spring Reactive Stack

Spring's popular servlet stack comprising of Spring MVC uses the conventional methods of accessing and processing data as synchronous calls. With the introduction of Spring 5, Spring's reactive stack was built on top of Reactor Core.

Spring's reactive stack offers additional support for Netty and Servlet 3.1+ containers, giving increased performances for reactive applications:

spring webflux reactor
Credit Spring

Spring WebFlux

Spring WebFlux is a counterpart module for Spring MVC. Where Spring MVC implements synchronous, blocking I/O, Spring WebFlux implements reactive programming via Reactive Streams.

You'll typically use one or the other, though you're also free to combine them together.

Spring WebFlux Dependencies

With a plain Spring Boot project initialized through Spring Initializr, it's enough to add a single dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <version>{version}</version>
</dependency>

The spring-boot-starter-webflux includes spring-web, spring-webflux, spring-boot-starter, spring-boot-starter-reactor-netty, etc., so there's no need for any other dependency to be present.

For the applications using Gradle for dependency management, Spring WebFlux can be added in the application's build.gradle file:

compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '2.2.2.RELEASE'

Mono and Flux

In Spring WebFlux, the data returned from any operation is packed into a reactive stream. There are two types that embody this approach and are the building blocks in WebFlux applications - Mono and Flux.

Mono is a stream which returns zero items or a single item (0..1), whereas Flux is a stream which returns zero or more items (0..N).

Mono is therefore used when you're expecting a single (or none) result, such as retrieving a unique user from the database, whereas Flux is used when you're expecting multiple results or a collection of some sort.

Spring WebFlux Controller

Similar to how we use controllers in classic Spring MVC, for creation of asynchronous REST APIs, we use WebFlux controller. Even the naming conventions are similar to ensure an easy transition between these two approaches.

To mark a class as a controller, we use the @RestController annotation on a class level.

Having Spring WebFlux and the Reactor Core dependencies in the class path will let Spring know that the @RestController is in fact a reactive component and add support for Mono and Flux.

Spring WebFlux Configuration

As is standard with Spring Boot, we'll handle the configuration through annotations. The @Configuration and @EnableWebFlux annotations mark a class as a configuration class and Spring's bean management will register it:

@Configuration
@EnableWebFlux 
public class WebFluxConfig {}

To use or extend the existing WebFlux configuration API, you can extend the WebFluxConfigurer interface:

@Configuration
@EnableWebFlux 
public class WebFluxConfig implements WebFluxConfigurer {}

CORS with Spring WebFlux

WebFlux also offers CORS (Cross-Origin Resource Sharing) support, very similar to the Spring MVC servlet stack. The CORS configuration can be set at project level, as well as controller and controller method levels.

To add the CORS configuration at project level you need to @Overrride the addCorsMappings() method from the WebFluxConfigurer interface:

@Configuration
@EnableWebFlux 
public class WebFluxConfig implements WebFluxConfigurer { 
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://www.stackabuse.com")
            .allowedMethods("GET", "PUT", "DELETE")
            .allowedHeaders("testHeader")
            .allowCredentials(true);
    }
}

And for adding CORS configuration at a more granular level, the @CrossOrigin annotation is used. This allows for specifying CORS details at controller and method level.

When @CrossOrigin is used at the controller class level, all the method level CORS settings inherit from the class level configuration:

@CrossOrigin(origins = "https://www.stackabuse.com")
@RestController
@RequestMapping("/resource") 
public class ResourceController {

    @GetMapping("/{id}")
    public Mono<Resource> getResource(@PathVariable String id) {

    }
}

Security with Spring Webflux

Spring also offers the standard security options for its WebFlux framework. You need to add @EnableWebFluxSecurity at the class level to enable the security configurations in the project:

@EnableWebFluxSecurity
public class WebfluxSecurity {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(
      ServerHttpSecurity http) {
        http.csrf().disable()
          .authorizeExchange()
          .pathMatchers(HttpMethod.GET, "/resource/").hasRole("ADMIN")
          .pathMatchers("/**").permitAll()
          .and()
          .httpBasic();
        return http.build();
    }
}

Spring WebFlux Web Client

Spring WebFlux also includes a reactive web client for managing REST calls. Reactor-Netty is used as default for communicating with the WebFlux server.

The WebFlux client object can be created using either static factory methods or through its builder method (more customization).

The static factory methods are WebClient.create() and WebClient.create(String baseUrl). On the other hand the WebClient.builder offers following options to add more details to the web client object:

  • uriBuilderFactory
  • defaultHeader
  • defaultCookie
  • defaultRequest
  • filter
  • exchangeStrategies
  • clientConnector

We'll take a closer look at this in the proceeding section where a demo application is built.

Demo Application

We'll be creating a simple reactive REST API using WebFlux's standard components, which will act as a reactive server. Then, a reactive web client application will be built, which retrieves information from this server and processes the data.

WebFlux Web Server

Repository

Let's start by laying down the foundation for a reactive application by creating a reactive repository interface.

public interface ResourceRepository extends ReactiveCrudRepository<Resource, String> {}

We have extended our repository from WebFlux's ReactiveCrudRepository, which will return data as Mono or Flux datatypes, depending on the number of elements which can be retrieved.

To use MongoDB with Spring WebFlux, we'll add a configuration class which tells Spring that the database should be handled as a reactive component:

@EnableReactiveMongoRepositories
public class MongoDbConfiguration extends AbstractReactiveMongoConfiguration {

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        return "testDatabase";
    }
}

Controller

With our data layer done and configured, let's create a simple REST controller which will retrieve Mono and Flux resources through GET requests:

@RestController
@RequestMapping("/resource")
public class ResourceController {

    @Autowired
    ResourceRepository resourceRepository;

    @GetMapping("/{id}")
    public Mono<Resource> getResource(@PathVariable String id) {
        return resourceRepository.findById(id);
    }

    @GetMapping
    public Flux<Resource> getResources() {
        return resourceRepository.findAll();
    }
}

Here we are using our reactive ResourceRepository to find a resource using the id coming in from the request. Since we're looking for a unique ID, the expected result is either 1 record (the resource with the passed ID is found), or 0 records (there are no records in the database), hence Mono is used as a return type.

Since findAll() can return more that one resource element (if present), Flux is used as the return type.

WebFlux Web Client

Now that we have a basic REST application set up, let's create a Spring WebFlux client which can send requests to the a WebFlux REST application.

To start a web client we need to create a WebClient object using the server URL:

public WebClient openConnection(String url) {
    client = WebClient.create(url);
    return client;
}

Once the WebClient object is created, we can use it to make requests to the web server.

Let's make a request to the server, retrieving a resource with the given ID:

public void getResourceById(String id) {
    Mono<Resource> result = client.get()
            .uri("/resource/{id}", "1")
            .retrieve()
            .bodyToMono(Resource.class);

    result.subscribe(System.out::println);
}

The bodyToMono() method is responsible for packing the body of the response into a Mono.

Similarly, if the calling endpoint returns data as Flux then we can retrieve it using the following method:

public void getAllResources() {
    Flux<Resource> result = client.get()
            .uri("/resource")
            .retrieve()
            .bodyToFlux(Resource.class);

    result.subscribe(System.out::println);
}

Conclusion

The Spring Framework allows developers to make reactive, non-blocking applications and APIs using the Spring's WebFlux stack. WebFlux offers annotations very similar to the ones used in classic Spring MVC applications which makes it easier for the developers to transition to reactive code.

In this guide, we've gone over the most important concepts of the WebFlux framework and built a demo application to show them in practice.

The source code can be found on GitHub.