developers

Introduction to Spring WebFlux

This article introduces the reader to reactive programming using the Spring WebFlux API.

Dec 6, 201818 min read

TL;DR: In this article, you will learn about reactive programming, why it is important, and when to apply it to your projects. You will do so by learning about how reactive programming came to be and its implementation in Spring Boot via the Spring WebFlux API. To better understand these concepts you will build a reactive RESTful API using Spring WebFlux and secure it using OAuth 2.0. You can find the project built throughout this article in this GitHub repository.

Prerequisites

To follow this article along, you will need some previous experience with Java. Also, if you have some previous experience with Spring, that would be helpful.

Now, in relation to your development environment, you will need to have the following installed:

  • JDK 8 or any version above (JDK 10 was used in this article).
  • Any text editor or IDE of your choice (IntelliJ recommended).

Theoretical Foundation for Spring WebFlux

Problem Statement: Blocking I/O

Traditionally developers have always done I/O on the main thread of their applications. This raises a flag because it will block the execution of your program. To solve this problem, you can do I/O on a separate execution thread, but since threads take a lot of memory, this solution will not scale with many I/O operations. Additionally, this solution is still blocking.

Solution: Non-Blocking Asynchronous I/O

To solve this problem, you will need a single thread and an event loop just like Node.js. This will guarantee asynchronous and non-blocking I/O operations. Spring Boot implements this solution through Project Reactor and the reactive programming paradigm. Before you move into playing with Project Reactor, it will be good to understand its foundations.

What is Reactive Programming?

According to Jonas Bonér and Viktor Klang, reactive programming, not to be confused with functional reactive programming, is a subset of asynchronous programming and a paradigm where the availability of new information drives the logic forward rather than having control flow driven by a thread-of-execution. So it is a programming paradigm that allows systems to function based on the availability of new information or data. This definition of reactive programming leads us directly to asynchronous programming and non-blocking programming.

What is Asynchronous Programming?

Asynchronous programming is paradigm which allows developers to write code that can respond to future events or change in the state of your program. The best example of this kind of programming comes from JavaScript. As an example, the

setTimeout
function, waits for a given number of milliseconds (a second is a thousand milliseconds) and then calls a function.

setTimeout(() => console.log("Tick"), 500);
console.log("Hello");

If you run the following code, you will realize that "Tick" is printed after "Hello" because the first argument to

setTimeout
is a callback that will be called after 500 milliseconds. In other words, after "Hello" gets printed. Using this callback principle, you can access shared resources without blocking your program execution. To support asynchronous programming, the API of reactive libraries are either callback or declarative/functional (through functions like map, filter and reduce) based. Due to callback hell, many reactive libraries are declarative/functional based.

What is Non-Blocking Programming?

In order to understand non-blocking I/O, you need to understand what blocking I/O is. Traditionally, access to a database or operating system resources will block the main thread of execution and only continue execution once that operation is done. This is blocking I/O. Now with the support of reactive database drivers in MongoDB and relational stores via projects like the R2DBC project Java web application can now access these resources without blocking your thread of execution.

Benefits of Reactive programming

  • Primarily, increase utilization of resource on multi-core and multi-CPU hardware.
  • According to Jonas Bonér and Viktor Klang reactive programming increases developer productivity because unlike traditional programming paradigms, reactive programming provides ways for dealing with asynchronous non-blocking computation and I/O.
  • Last but not the least, reactive programming shines well in the creation of components for a reactive system. There are systems that are responsive, resilient, elastic and message driven.

Reactive Programming with Reactive Streams

To support Reactive programming, the team behind Spring Boot created a library called Project Reactor which implements the specifications of the Reactive Streams initiative. This is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure. This standard is specified through the following interface:

  • Processor<T,R>
    • This class represents a processing stage, which is both a
      Subscriber
      and a
      Publisher
      and obeys the contracts of both.
  • Publisher<T>
    • This is a provider of a potentially unbounded number of sequenced elements, publishing them according to the demand received from its
      Subscribers
      .
  • Subscriber<T>
    • Instances of this class will receive calls to
      Subscriber.onSubscribe(Subscription)
      once after passing an instance of Subscriber to
      Publisher.subscribe(Subscriber)
  • Subscription
    • This class represents a one-to-one lifecycle of a Subscriber subscribing to a Publisher.

Reactive Streams Data Flow

While dealing with reactive streams, the data flow as follows:

  1. The
    subscribe
    method is called on a
    Publisher
    instance.
  2. Then a
    Subscription
    object is created and the
    onSubscribe
    method of the
    Subscriber
    is executed with the
    Subscription
    object.
  3. After that, a
    Subscriber
    will call the
    request
    method in the
    Subscription
    class to specify the number of objects it can process (If this method is not called explicitly, an unbounded number of objects is requested).
  4. Then the
    Subscriber
    can receive objects via the
    onNext
    method. If the Subscriber receives all the objects it requested, it can request more object or cancel the Subscription by calling
    onComplete
    . If at some point there is an error the
    Publisher
    calls the
    onError
    method on the
    Subscriber
    .

For more details about the Reactive Streams interface check out this resource.

Project Reactor's implementation of Reactive Streams

Project Reactor implements two data types for the Publisher interface: Flux and Mono. Flux is for a list of objects and Mono for zero or one object. These implementations support operators like

map
,
filter
,
reduce
, and
flatMap
which maps every entry in a Publisher to another Publisher. Last but not least, in order to get data from a publisher(Flux or Mono) you need to call the
subscribe
on it.

What is Spring WebFlux

To support reactive programming and the creation of reactive systems, the Spring Boot team created a whole new web stack called Spring WebFlux. This new web stack supports annotated controllers, functional endpoints, WebClient (analogous to

RestTemplate
in Spring Web MVC), WebSockets and a lot more.

Developing a RESTful API with Spring WebFlux

In this section, you will learn some key features of Spring WebFlux by creating a movie REST API with it. For starters, go to the Spring Initializr page and fill out the form like this:

  • Generate a: At the top of the page, choose "Gradle Project".
  • Group: You can leave this field as
    com.example
    .
  • Artifact: You can type
    movie-api
    in this one.

Then, you will have to add the following dependencies (through the Search for dependencies field):

  • Reactive Web
  • Security
  • Embedded MongoDB
  • Reactive MongoDB

Generating Spring Boot project with Spring Initializr

After filling out the form, click on the Generate Project button. This will download your project for you. Then, unzip the downloaded file and open your project in your favorite IDE. After that, navigate to your

build.gradle
file in the project's root directory.

Notice that your Embedded MongoDB dependency (

de.flapdoodle.embed:de.flapdoodle.embed.mongo
) has been added as a
testImplementation
dependency. Spring Boot added this dependency as a test dependency because it expects you to use this database in development and testing, and not production. For the sake of this tutorial, make this dependency an
implementation
dependency. After the change, your dependency section should look like the following:

//.... rest of file

dependencies {
    // ... don't remove the other two dependencies ...
    implementation('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
}

Next, you need to define a

Movie
entity to represent movies in your database. To do that, start by creating a package called
model
inside the
com.example.movieapi
package. In that package, create a class called
Movie
and paste the following content in it:

package com.example.movieapi.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.time.LocalDateTime;
import java.util.Objects;

@Document
public class Movie {

  @Id
  private String id;

  private String name;

  private String genre;

  private LocalDateTime releaseDate;

  public Movie() {
  }

  public Movie(String id, String name, String genre, LocalDateTime releaseDate) {
      this.id = id;
      this.name = name;
      this.genre = genre;
      this.releaseDate = releaseDate;
  }

  public String getId() {
      return id;
  }

  public void setId(String id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getGenre() {
      return genre;
  }

  public void setGenre(String genre) {
      this.genre = genre;
  }

  public LocalDateTime getReleaseDate() {
      return releaseDate;
  }

  public void setReleaseDate(LocalDateTime releaseDate) {
      this.releaseDate = releaseDate;
  }

  @Override
  public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Movie movie = (Movie) o;
      return Objects.equals(id, movie.id) &&
              Objects.equals(name, movie.name) &&
              Objects.equals(genre, movie.genre) &&
              Objects.equals(releaseDate, movie.releaseDate);
  }

  @Override
  public int hashCode() {
      return Objects.hash(id, name, genre, releaseDate);
  }

  @Override
  public String toString() {
      return "Movie{" +
              "id='" + id + '\'' +
              ", name='" + name + '\'' +
              ", genre='" + genre + '\'' +
              ", releaseDate=" + releaseDate +
              '}';
  }
}

This class is annotated with

@Document
because MongoDB is a document database. At the time of this writing, Spring Boot does not support reactive database drivers for relational databases, which is why I decided to go with MongoDB.
@Id
is used to annotate the instance variable you will use as a key in your database.

Next, you need to create a repository for querying your MongoDB database in a reactive fashion. Luckily, Spring now supports reactive repositories. First, create a

repository
package inside the
com.example.movieapi.model
package. Then, create a
MovieRepository
interface in this package. Fill this class with the following content:

package com.example.movieapi.model.repository;

import com.example.movieapi.model.Movie;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

public interface MovieRepository extends ReactiveMongoRepository<Movie, String> {
}

Notice that this repository extends the

ReactiveMongoRepository
instead of the traditional
MongoRepository
. As usual, this repository interface provides out of the box implementation for methods like
findById
,
findAll
,
save
, and many more methods.

With that covered, you can now query your database and get results. Next, you need to expose these database queries via RESTful endpoints. For starters, create a

controller
package in your
com.example.movieapi
package. In this package, create a file called
MovieController
and add the following code to it:

package com.example.movieapi.controller;

import com.example.movieapi.model.Movie;
import com.example.movieapi.model.repository.MovieRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/movies")
public class MovieController {

    private MovieRepository movieRepository;

    public MovieController(MovieRepository movieRepository){
        this.movieRepository = movieRepository;
    }

    @GetMapping
    public Flux<Movie> getMovies(){
        return movieRepository.findAll();
    }

    @GetMapping("{id}")
    public Mono<ResponseEntity<Movie>> getMovie(@PathVariable String id){
        return movieRepository.findById(id)
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }


    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<Movie> saveMovie(@RequestBody Movie movie){
        return movieRepository.save(movie);
    }

    @PutMapping("{id}")
    public Mono<ResponseEntity<Movie>> updateMovie(@PathVariable(value = "id") String id, @RequestBody Movie movie){
        return movieRepository.findById(id)
                .flatMap(existingMovie -> {
                    existingMovie.setName(movie.getName());
                    existingMovie.setGenre(movie.getGenre());
                    existingMovie.setReleaseDate(movie.getReleaseDate());
                    return movieRepository.save(existingMovie);
                })
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    @DeleteMapping("{id}")
    public Mono<ResponseEntity<Void>> deleteMovie(@PathVariable(value = "id") String id){
        return movieRepository.findById(id)
                .flatMap(existingMovie ->
                    movieRepository.delete(existingMovie)
                            .then(Mono.just(ResponseEntity.ok().<Void>build()))
                )
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    @DeleteMapping
    public Mono<Void> deleteAllMovies(){
        return movieRepository.deleteAll();
    }
}

You need to examine these methods carefully. Notice how they do not return

void
or a
Movie
type but instead
Void
or
Movie
either wrapped by
Flux
or
Mono
which are publishers from Project Reactor added in the
spring-boot-starter-webflux
dependency. To help you better understand Project Reactor's operators, I will give a detailed explanation of each method in this controller:

  • The
    getMovies
    method calls
    findAll
    on your repository and returns a
    Flux
    of movies. Notice that the
    subscribe
    method has not been called on the
    Flux
    to get the values. Spring Boot will call this method automatically when this endpoint is accessed.
  • The
    getMovie
    method calls
    findById
    on your repository which returns a
    Mono
    of a movie. Then the
    map
    operator is called on this movie to wrap it in a
    ResponseEntity
    object with status code 200 OK, and finally there is a call to
    defaultIfEmpty
    to build an empty
    ResponseEntity
    with status 404 NOT FOUND if the movie was not found.
  • The
    saveMovie
    method calls the
    save
    method on your repository to save a new movie object in the database.
  • The
    updateMovie
    method takes in the id of the movie you want to update with an object that represents the values of the updated movie and then calls
    findById
    to get the movie you want to update, then calls
    flatMap
    with this movie to update its entries using its setters and the values from the movie passed as argument. Then it saves them to the database and wraps this updated movie in a
    ResponseEntity
    with status code 200 OK in case of success or 404 NOT FOUND in case of failure.
  • deleteMovie
    this method follows the same flow as
    updateMovies
    . First, you search the movie you want to delete. Next, you delete and return 200 OK to show your delete was successful or you return 404 NOT FOUND to say the movie was not found.
  • deleteAllMovies
    this method calls the
    deleteAll
    method on your movie repository which empties our database.

With this controller in place, you now have all the functionality to access your data but you do not have data in your database. To solve that, go ahead and add data to your database by updating the

MovieApiApplication
class as follows:

//... leave all other library imports untouched
import com.example.movieapi.model.Movie;
import com.example.movieapi.model.repository.MovieRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import reactor.core.publisher.Flux;

import java.time.LocalDateTime;

@SpringBootApplication
public class MovieApiApplication {

    // ... static void main ...

    @Bean
    CommandLineRunner init(ReactiveMongoOperations operations, MovieRepository movieRepository) {
        return args -> {
            Flux<Movie> productFlux = Flux.just(
                    new Movie(null, "Avenger: Infinity Wars", "Action", LocalDateTime.now()),
                    new Movie(null, "Gladiator", "Drama/Action", LocalDateTime.now()),
                    new Movie(null, "Black Panther", "Action", LocalDateTime.now()))
                    .flatMap(movieRepository::save);

            productFlux
                    .thenMany(movieRepository.findAll())
                    .subscribe(System.out::println);
        };
    }
}

This code creates a

Flux
of three sample Movie objects, saves them to the database. Then, queries all the movies from the database and print them to the console.

Now, you can run your RESTful API by either issuing the command

./gradlew bootRun
from the command line or by building and running the project in your favorite IDE. If everything works as expected, your RESTful Spring Boot API will be up and running. To test it, you can use a tool like Postman or
curl
to issue requests to the available endpoints. But before you start testing, comment out the
spring-boot-starter-security
in your
build.gradle
file, this will remove the default security on your endpoints.

Run the following commands to test your API:

# issue a GET request to see the list of movies
curl http://localhost:8080/movies

# issue a GET request to see a particular movie, get an {id}
# from one of the movies printed in the command line
curl http://localhost:8080/movies/{movie-id}

# issue a POST request to create a new movie
curl -H "Content-Type: application/json" -X POST -d '{"name":"Hercules","genre":"Drama/Action","releaseDate":"2018-11-22T14:15:18.512"}' http://localhost:8080/movies


# issue a PUT request to update the genre of the Gladiator movie. Get {movie-id} from the command line.
curl -H "Content-Type: application/json" -X PUT -d '{"id":"{movie-id}","name":"Gladiator","genre":"{new-genre}","releaseDate":"{movie-release-date}"}' http://localhost:8080/movies/{movie-id}

# issue a DELETE request for a movie
curl -X DELETE http://localhost:8080/movies/{movie-id}

# issue a DELETE request to delete all movies
curl -X DELETE http://localhost:8080/movies

Securing your Spring WebFlux API with Auth0

In this section, you are going to secure your API with Auth0's OAuth 2.0 implementation. You'll need to sign up for a free Auth0 account here. After signing up for your Auth0 account, you will need to create an API on Auth0 to represent your Spring Boot API and to be able to configure it to authenticate requests. To do this, head to the APIs section on your Auth0 dashboard and click on the Create API button. After that, the dashboard will show you a form where you will have to enter:

  • a name for your API (this can be something like "Movies API");
  • an identifier (in this case, it can be
    https://localhost:8080/movies
    or anything that resembles a valid URL);
  • and the signing algorithm (for this field, make sure you choose
    RS256
    ).

Then, you can create your Auth0 API by clicking on the Create button. Now, that you have created your Auth0 API, you need to add security configuration to your Spring Boot project. For this, create a class called

SecurityConfig
in your
com.example.movieapi
package. Add the following code to this file:

package com.example.movieapi.security;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
import org.springframework.security.web.server.SecurityWebFilterChain;

@EnableWebFluxSecurity
public class SecurityConfig {

    @Value("${spring.security.oauth2.resourceserver.jwk.issuer-uri}")
    private String issuerUri;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http){
        http
                .authorizeExchange()
                .pathMatchers(HttpMethod.GET,"/movies/**").permitAll()
                .anyExchange().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt();
        return http.build();
    }

    @Bean
    public ReactiveJwtDecoder jwtDecoder() {
        return ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri);
    }
}

Note: You will have to uncomment the security dependency (

spring-boot-starter-security
) on your
build.gradle
file to be able to import the dependencies above.

  • The
    jwtDecoder
    method in the above class is used by your API to decode JWT access token and verify them.
  • The
    securityWebFilterChain
    method is used to restrict unauthenticated users of your API to GET operations and request authentication for any other operation.

To support OAuth authentication you need to add the following dependencies to your

build.gradle
file.

//.... rest of file

dependencies {
    // ... don't remove the other two dependencies ...
  compile('org.springframework.security:spring-security-oauth2-jose')
  compile('org.springframework.security:spring-security-oauth2-resource-server')
}

Finally, you have to add Auth0 configuration to your Spring Boot project so that it knows where to verify your JWT access tokens. To do this, go to the

resources
folder of your project and rename your
application.properties
file to
application.yml
and add the following configurations to it:

spring:
  security:
    oauth2:
      resourceserver:
        id: http://localhost:8080/movies
        jwk:
          jwk-set-uri: https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json
          issuer-uri: https://YOUR_AUTH0_DOMAIN/

Make sure you replace both

YOUR_AUTH0_DOMAIN
placeholders with your own Auth0 domain (e.g.,
blog-samples.auth0.com
). Also, if you have chosen another identifier for your Auth0 API (rather than
http://localhost:8080/movies
), make sure you update the
id
property accordingly in the file above.

Testing Your API

With that in place, your API is now secured. You will still be able to read from your API but not carry out write operations. To interact with your endpoints you need an access token. The strategy to get one usually used for this depends on the type of client application you are developing. You can read more about which authentication flow strategy to use at Auth0. For this simple test, you can get an access token from your API dashboard. To do so, go to the APIs section in your Auth0 dashboard, click on the API you created before, and then click on the Test section of this API. There, you will find a button called Copy Token. Click on this button to copy an access token to your clipboard

Copying a test access token to clipboard from Auth0 API

After copying the token, open your terminal and run your application:

./gradlew bootRun
. Then, open another terminal and run the following commands (make sure to replace
<YOUR-ACCESS-TOKEN-HERE>
placeholder with your access token):

# set your access token variable
ACCESS_TOKEN=<YOUR-ACCESS-TOKEN-HERE>

# issue a POST request to create a new movie
curl -H "Content-Type: application/json" -H "Authorization: Bearer "$ACCESS_TOKEN -X POST -d '{"name":"Hercules","genre":"Drama/Action","releaseDate":"2018-11-22T14:15:18.512"}' http://localhost:8080/movies

# issue a PUT request to update the genre of the Gladiator movie
curl -H "Content-Type: application/json" -H "Authorization: Bearer "$ACCESS_TOKEN -X PUT -d '{"id":"{movie-id}","name":"Gladiator","genre":"{new-genre}","releaseDate":"{movie-release-date}"}' http://localhost:8080/movies/{movie-id}

# issue a DELETE request for a movie
curl -H "Authorization: Bearer "$ACCESS_TOKEN -X DELETE http://localhost:8080/movies/{movie-id}

# issue a DELETE request to delete all movies
curl -H "Authorization: Bearer "$ACCESS_TOKEN -X DELETE http://localhost:8080/movies

Finally go back and run the

curl
commands you ran before securing your API. This time make sure you pass a
-i
flag to the command after
curl
. As you will see, GET requests will succeed as before but other requests will return an unauthorized request error.

Conclusion

In this article, you learned about the motivation behind reactive programming and how it is implemented in Spring WebFlux with Project Reactor. You also learned how to use the Spring WebFlux API by developing a RESTful API and securing it with Auth0. Developing with Spring WebFlux is not hard, it just requires a change in mindset since you are developing with a different paradigm from Spring Web MVC. It also has a lot in common with Spring Web MVC, so transitioning will not be very difficult.

Although you used annotated controllers to develop your API, you can also develop it in a functional and declarative style using functional endpoints.

In this article, you just scratched the surface of Spring WebFlux. However, this framework supports many other amazing features like WebSockets, WebClient, and functional endpoints. To learn more on the topic visit the official documentation.