Spring Web Reactive

Spring Web Reactive – REST Controllers

Wojtek Łowiec 04.01.2017

Since early 2016 the new major release of the Spring Framework has been under development. The fifth version will bring many new features and in particular a reactive version of its web framework – Spring Web Reactive. Here at Sparkbit we rely heavily on the time-tested Spring MVC module to create RESTful APIs for our backend systems, which means we are very curious what will the reactive approach bring to the table for our most common use cases.

Example problem

Let’s come up with an example problem that we want to solve using the new reactive approach. Imagine we want to create a system controlling the process of delivering pizzas: from an online web page allowing users to order pizzas to a monitoring system for the fleet of delivery scooters. We would like to implement a REST API that will be consumed by a frontend web app and by native mobile apps. To monitor a scooter the native app would periodically send a set of GPS coordinates to our backend system. Let’s see how this could be achieved in the “old” style:

Good Old MVC

public class LocationController {

private final static Logger LOG = LoggerFactory.getLogger(LocationController.class);
private final LocationService locationService;

public LocationController(LocationService locationService) {
    this.locationService = locationService;

@PostMapping(path = "location", consumes = MediaType.APPLICATION_JSON_VALUE)
public void logLocation(@Valid @RequestBody List<LatLng> locations) {
    LOG.info("Received request");
    LOG.info("Request processed");

We can see here that we handle a POST request to the location resource with a JSON payload.

Spring Web Reactive style

public class LocationController {
private final static Logger LOG = LoggerFactory.getLogger(LocationController.class);
private final ReactiveLocationService locationService;

public LocationController(ReactiveLocationService locationService) { 
    this.locationService = locationService; 

@PostMapping(path = "location", consumes = MediaType.APPLICATION_JSON_VALUE) 
public void logLocation(@Valid @RequestBody Flux<LatLng> locations) { 
    LOG.info("Received request"); 
    LOG.info("Request processed");

Here we assume that the ReactiveLocationService accepts an argument of type Flux<LatLng>.

What’s the difference?

The only difference is of course the change in the method signature. The reactive version uses a generic abstract class called Flux as the input. This class is a part of Project Reactor  and it’s a Java implementation of Reactive Streams used in the Spring Framework 5. We won’t be going through the details of what reactive streams are but in this example the Flux class represents the stream of JSON encoded objects from the HTTP request that’s being handled in the method. The data in the stream is evaluated asynchronously and this is key. Let’s mimic a slow client of our service to highlight this point:

public class SlowLocationClient {

    private static final Logger LOG = LoggerFactory.getLogger(SlowLocationClient.class);

    public static void main(String[] argv) {
        ClientHttpConnector httpConnector = new ReactorClientHttpConnector();
        ClientRequest<Flux<LatLng>> request = ClientRequest.POST("http://localhost:8080/location")
                .body(BodyInserters.fromPublisher(getPayload(), LatLng.class));
        LOG.info("Sending request");
                .doOnNext(cr -> LOG.info("Received {}", cr.statusCode())).block();
        LOG.info("Received response");

    private static Flux<LatLng> getPayload() {
        return Flux.just(new LatLng(52.1333656, 21.0633831))

This client will send a request but it will take 10 seconds to send the full payload.

Additionally, let’s assume that the ReactiveLocationService has the following implementation:

 void storeCoordinates(Flux<LatLng> coordinates) {
        coordinates.map(latLng -> {
            LOG.info("Stored {}", latLng);
            return latLng;

On the server side we will see the following log trace:

12:07:50.548 [reactor-http-nio-4] INFO p.sparkbit.server.LocationController – Received request
12:07:50.548 [reactor-http-nio-4] INFO p.sparkbit.server.LocationController – Request processed
12:08:00.564 [reactor-http-nio-2] INFO p.s.server.ReactiveLocationService – Stored LatLng{lat=52.1333656, lng=21.0633831}

Notice that the controller started to handle the request without waiting for the full payload. This means we can do other work while the body of a request is being parsed (or sent like in this example).

Another thing to notice is that the thread that is running the controller method ends the execution of the method before the ReactiveLocationService stores the location.

From threads to streams

As we can see from the example above a mere 4 letter change in the signature shifts the programming paradigm to a reactive one. We can no longer think in terms of a linear execution model where one request is handled by one thread. The reactive streams will be handled by a lot of threads in their lifecycle. This complicates things when we migrate from the old MVC framework. We no longer can rely on thread affinity for things like the security context or transaction handling. A closer example of a difficulty arising from the streaming model is error handling. In the older version of Spring Web we could define a method:

public void handleException(Exception e) {
    LOG.error("Error! {}", e);

in a class annotated as a @ControllerAdvice and this allowed us to handle a lot of common exceptions that would be thrown from our controllers. The situation with Spring Web Reactive is more complicated. Because the reactive streams are evaluted by a different thread than the one that executes the controllers method, the exceptions won’t be propagated to the controller thread automatically. This means that the @ExceptionHandler method will work only for exceptions that are thrown in the thread that handles the request directly. Exceptions thrown in the stream will have to be propagated back to the thread if we want to use the @ExceptionHandler feature. This seems like a bit of a let down but at the time of writing this Spring 5 is still not released so error handling might still get better.

What about output?

We’ve seen reactive streams as an input but what about outputs? Methods for request mappings of course can also have reactive types like Flux.

    @PostMapping(path = "location/address", consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<Address> fetchAddresses(@Valid @RequestBody Flux<LatLng> locations) {
        LOG.info("Received request");
        try {
            return locationService.getAddresses(locations);
        } finally {
            LOG.info("Request processed");

Let’s assume that the ReactiveLocationService has the following implementation:

    Flux<Address> getAddresses(Flux<LatLng> coordinates) {
        return Flux.interval(Duration.ofSeconds(5))
                .zipWith(coordinates, (no, latLng) -> {
                    LOG.info("Retrieving address for {}", latLng);
                    return new Address("Rome");

The example is pretty simple. For every coordinate we would like to  return the current address for our pizza delivery people. As we know all roads lead to Rome so we will return “Rome” for every stream element. The output would be serialized to a JSON array if we configured our app correctly. We can also see a delay implemented in the locationService which emulates a slow server-side operation. A possible log output we could see is:

12:16:59.096 [reactor-http-nio-4] INFO p.sparkbit.server.LocationController – Received request
12:16:59.096 [reactor-http-nio-4] INFO p.sparkbit.server.LocationController – Request processed
12:17:04.097 [timer-1] INFO p.s.server.ReactiveLocationService – Retrieving address for LatLng{lat=41.9102411, lng=12.395572}
12:17:09.097 [timer-1] INFO p.s.server.ReactiveLocationService – Retrieving address for LatLng{lat=41.9102411, lng=12.395572}
12:17:14.097 [timer-1] INFO p.s.server.ReactiveLocationService – Retrieving address for LatLng{lat=41.9102411, lng=12.395572}

As we can see the output of the controller method is also evaluated after its execution in a different thread too!

Using RxJava

The Java world contains other Reactive Streams implementations beyond Project Reactor. Probably the most popular one is RxJava with its Observable class. As it turns out Spring Web Reactive can work with it too, assuming the correct Rx modules are properly set up. You can read more about the Spring Web Reactive in its documentation.

Functional Web Framework

Another feature concerning HTTP requests handling is a functional web framework. The basic idea is to have a way to define the routes (or resources in a REST example) as functions and chain them together. The advantage of this approach is simplicity and reduction of boilerplate code when all you want to create is a very small service. There are a few Micro Web frameworks out there that have this approach (Spark) .

RouterFunction<?> route = 
            request -> ServerResponse.ok().body(request.bodyToFlux(LatLng.class)
        .map(ll -> new Address("Rome")), Address.class))
                request -> ServerResponse.ok().body(Mono.just("home"), String.class)));
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter =
        new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", 8080);
LOG.info("Press ENTER to exit.");

This example shows the same resource we implemented using the annotation based style plus a GET route for the current location. More on this feature can be found on the Spring blog entry about this very topic.

Testing out controllers

We can test our controllers using the SpringBootTest mechanism. This will run an Spring Web Reactive app with the classes / context we will give it in the tests. We will use the reactive web client that’s available in Spring 5 to run requests against it:

        classes = {LocationController.class, Spring5DemoApplication.class},
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Spring5DemoApplicationTests {

    private WebClient webClient;

    private int port;

    public void setup() {
        this.webClient = WebClient.create(new ReactorClientHttpConnector());

    public void homeController() {
        LatLng ll = new LatLng(20d, 52d);

        ClientRequest<Flux<LatLng>> request = ClientRequest
                .POST("http://localhost:{port}/location/address", this.port)
                .body(Flux.just(ll), LatLng.class);

        Flux<Address> result = this.webClient
                .flatMap(response -> response.bodyToFlux(Address.class));

                .consumeNextWith(address -> {
                    assertEquals("Rome", address.getCity());

As we can see we are just doing a POST request to the “location/address”  resource with the web client. The client itself will respond with a Flux object that we can verify with an expectation that it will complete. It remains to be seen if we can get something like a MockMvc for the Spring Web Reactive module.



The Spring Web Reactive module is a big step in embracing the Reactive Programming paradigm. Although the work is still in progress (a release candidate is scheduled for Q1 2017) you can start playing with the new Spring Framework version. A good starting point would be the awesome Spring Boot starter project created specially for the reactive web framework that can be found here. In future posts we will dive deeper into new features of the upcoming Spring 5 and how the old functionalities look like in the reactive paradigm. The code from this blog post is available as a public Git repository.

Komentarze: 0

Notice: Korzystanie z Motyw nieposiadający comments.php uznawane jest za przestarzałe od wersji 3.0.0! Nie istnieje żadna alternatywa. Proszę zawrzeć w motywie szablon comments.php. in /var/www/html/pl/wp-includes/functions.php on line 4597

3 odpowiedzi na “Spring Web Reactive – REST Controllers”

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *