23. Reactive Web Applications

This section provides basic information on the reactive programming support for Web applications in Spring Framework 5.

23.1 Introduction

23.1.1 What is Reactive Programming?

In plain terms reactive programming is about non-blocking applications that are asynchronous and event-driven and require a small number of threads to scale vertically (i.e. within the JVM) rather than horizontally (i.e. through clustering).

A key aspect of reactive applications is the concept of backpressure which is a mechanism to ensure producers don’t overwhelm consumers. For example in a pipeline of reactive components extending from the database to the HTTP response when the HTTP connection is too slow the data repository can also slow down or stop completely until network capacity frees up.

Reactive programming also leads to a major shift from imperative to declarative async composition of logic. It is comparable to writing blocking code vs using the CompletableFuture from Java 8 to compose follow-up actions via lambda expressions.

For a longer introduction check the blog series "Notes on Reactive Programming" by Dave Syer.

23.1.2 Reactive API and Building Blocks

Spring Framework 5 embraces Reactive Streams as the contract for communicating backpressure across async components and libraries. Reactive Streams is a specification created through industry collaboration that has also been adopted in Java 9 as java.util.concurrent.Flow.

The Spring Framework uses Reactor internally for its own reactive support. Reactor is a Reactive Streams implementation that further extends the basic Reactive Streams Publisher contract with the Flux and Mono composable API types to provide declarative operations on data sequences of 0..N and 0..1.

The Spring Framework exposes Flux and Mono in many of its own reactive APIs. At the application level however, as always, Spring provides choice and fully supports the use of RxJava. For more on reactive types check the post "Understanding Reactive Types" by Sebastien Deleuze.

23.2 Spring Web Reactive Module

Spring Framework 5 includes a new spring-web-reactive module. The module contains support for reactive HTTP and WebSocket clients as well as for reactive server web applications including REST, HTML browser, and WebSocket style interactions.

23.2.1 Server Side

On the server-side the new reactive module supports 2 distinct programming models:

  • Annotation-based with @Controller and the other annotations supported also with Spring MVC
  • Functional, Java 8 lambda style routing and handling

Both programming models are executed on the same reactive foundation that adapts non-blocking HTTP runtimes to the Reactive Streams API. The diagram below shows the server-side stack including traditional, Servlet-based Spring MVC on the left from the spring-web-mvc module and also the reactive stack on the right from the spring-web-reactive module.

web reactive overview

The new reactive stack can run on Servlet containers with support for the Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as Netty and Undertow. Each runtime is adapted to a reactive ServerHttpRequest and ServerHttpResponse exposing the body of the request and response as Flux<DataBuffer>, rather than InputStream and OutputStream, with reactive backpressure. REST-style JSON and XML serialization and deserialization is supported on top as a Flux<Object>, and so is HTML view rendering and Server-Sent Events.

Annotation-based Programming Model

The same @Controller programming model and the same annotations used in Spring MVC are also supported on the reactive side. The main difference is that the framework contracts underneath — i.e. HandlerMapping, HandlerAdapter, are non-blocking and operate on the reactive ServerHttpRequest and ServerHttpResponse rather than on the HttpServletRequest and HttpServletResponse. Below is an example with a reactive controller:

@RestController
public class PersonController {

	private final PersonRepository repository;

	public PersonController(PersonRepository repository) {
		this.repository = repository;
	}

	@PostMapping("/person")
	Mono<Void> create(@RequestBody Publisher<Person> personStream) {
		return this.repository.save(personStream).then();
	}

	@GetMapping("/person")
	Flux<Person> list() {
		return this.repository.findAll();
	}

	@GetMapping("/person/{id}")
	Mono<Person> findById(@PathVariable String id) {
		return this.repository.findOne(id);
	}
}

Functional Programming Model

The functional programming model uses Java 8 lambda style routing and request handling instead of annotations. The main API contracts are functional interfaces named RouterFunction and HandlerFunction. They are simple but powerful building blocks for creating web applications. Below is an example of functional request handling:

PersonRepository repository = ...

RouterFunctions
	.route(GET("/person/{id}").and(accept(APPLICATION_JSON)), request -> {
		int personId = Integer.valueOf(request.pathVariable("id"));
		Mono<ServerResponse> notFound = ServerResponse.notFound().build();
		return repository.findOne(personId)
				.then(person -> ServerResponse.ok().body(Mono.just(person), Person.class))
				.otherwiseIfEmpty(notFound);
	})
	.andRoute(GET("/person").and(accept(APPLICATION_JSON)), request ->
			ServerResponse.ok().body(repository.findAll(), Person.class))
	.andRoute(POST("/person").and(contentType(APPLICATION_JSON)), request ->
			ServerResponse.ok().build(repository.save(request.bodyToMono(Person.class))));

For more on the functional programming model see the M3 release blog post.

23.2.2 Client Side

Spring Framework 5 includes a functional, reactive WebClient that offers a fully non-blocking and reactive alternative to the RestTemplate. It exposes network input and output as a reactive ClientHttpRequest and ClientHttpRespones where the body of the request and response is a Flux<DataBuffer> rather than an InputStream and OutputStream. In addition it supports the same reactive JSON, XML, and SSE serialization mechanism as on the server side so you can work with typed objects. Below is an example of using the WebClient which requires a ClientHttpConnector implementation to plug in a specific HTTP client such as Reactor Netty:

WebClient client = WebClient.create(new ReactorClientHttpConnector());

ClientRequest<Void> request = ClientRequest
		.GET("http://example.com/accounts/{id}", 1L)
		.accept(APPLICATION_JSON)
		.build();

Mono<Account> account = client
		.exchange(request)
		.then(response -> response.bodyToMono(Account.class));
[Note]Note

The AsyncRestTemplate also supports non-blocking interactions. The main difference is it can’t support non-blocking streaming, like for example Twitter one, because fundamentally it’s still based and relies on InputStream and OutputStream.

23.2.3 Request and Response Body Conversion

The spring-core module provides reactive Encoder and Decoder contracts that enable the serialization of a Flux of bytes to and from typed objects. The spring-web module adds JSON (Jackson) and XML (JAXB) implementations for use in web applications as well as others for SSE streaming and zero-copy file transfer.

For example the request body can be one of the following way and it will be decoded automatically in both the annotation and the functional programming models:

  • Account account — the account is deserialized without blocking before the controller is invoked.
  • Mono<Account> account — the controller can use the Mono to declare logic to be executed after the account is deserialized.
  • Single<Account> account — same as with Mono but using RxJava
  • Flux<Account> accounts — input streaming scenario.
  • Observable<Account> accounts — input streaming with RxJava.

The response body can be one of the following:

  • Mono<Account> — serialize without blocking the given Account when the Mono completes.
  • Single<Account> — same but using RxJava.
  • Flux<Account> — streaming scenario, possibly SSE depending on the requested content type.
  • Observable<Account> — same but using RxJava Observable type.
  • Flowable<Account> — same but using RxJava 2 Flowable type.
  • Flux<ServerSentEvent> — SSE streaming.
  • Mono<Void> — request handling completes when the Mono completes.
  • Account — serialize without blocking the given Account; implies a synchronous, non-blocking controller method.
  • void — specific to the annotation-based programming model, request handling completes when the method returns; implies a synchronous, non-blocking controller method.

23.2.4 Reactive WebSocket Support

The Spring Framework 5 spring-web-reactive module includes reactive WebSocket client and server support. Both client and server are supported on the Java WebSocket API (JSR-356), Jetty, Undertow, Reactor Netty, and RxNetty.

On the server side, declare a WebSocketHandlerAdapter and then simply add mappings to WebSocketHandler-based endpoints:

@Bean
public HandlerMapping webSocketMapping() {
	Map<String, WebSocketHandler> map = new HashMap<>();
	map.put("/foo", new FooWebSocketHandler());
	map.put("/bar", new BarWebSocketHandler());

	SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
	mapping.setUrlMap(map);
	return mapping;
}

@Bean
public WebSocketHandlerAdapter handlerAdapter() {
	return new WebSocketHandlerAdapter();
}

On the client side create a WebSocketClient for one of the supported libraries listed above:

WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000);

23.3 Getting Started

23.3.1 Spring Boot Starter

The Spring Boot Web Reactive starter available via http://start.spring.io is the fastest way to get started. It does all that’s necessary so you to start writing @Controller classes just like with Spring MVC. Simply go to http://start.spring.io, choose version 2.0.0.BUILD-SNAPSHOT, and type reactive in the dependencies box. By default the starter runs with Tomcat but the dependencies can be changed as usual with Spring Boot to switch to a different runtime. See the starter page for more details and instruction

There is no Spring Boot Starter for the functional programming model yet but it’s very easy to try it out. See the next section on "Manual Bootstrapping".

23.3.2 Manual Bootstrapping

This section outlines the steps to get up and running manually.

For dependencies start with spring-web-reactive and spring-context. Then add jackson-databind and io.netty:netty-buffer (temporarily see SPR-14528) for JSON support. Lastly add the dependencies for one of the supported runtimes:

  • Tomcat — org.apache.tomcat.embed:tomcat-embed-core
  • Jetty — org.eclipse.jetty:jetty-server and org.eclipse.jetty:jetty-servlet
  • Reactor Netty — io.projectreactor.ipc:reactor-netty
  • RxNetty — io.reactivex:rxnetty-common and io.reactivex:rxnetty-http
  • Undertow — io.undertow:undertow-core

For the annotation-based programming model bootstrap with:

ApplicationContext context = new AnnotationConfigApplicationContext(DelegatingWebReactiveConfiguration.class);  // (1)
HttpHandler handler = DispatcherHandler.toHttpHandler(context);  // (2)

The above loads default Spring Web framework configuration (1), then creates a DispatcherHandler, the main class driving request processing (2), and adapts it to HttpHandler — the lowest level Spring abstraction for reactive HTTP request handling.

For the functional programming model bootstrap as follows:

ApplicationContext context = new AnnotationConfigApplicationContext(); // (1)
context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2)
context.registerBean(BarBean.class); // (3)

HttpHandler handler = WebHttpHandlerBuilder
		.webHandler(RouterFunctions.toHttpHandler(...))
		.applicationContext(context)
		.build(); // (4)

The above creates an AnnotationConfigApplicationContext instance (1) that can take advantage of the new functional bean registration API (2) to register beans using a Java 8 Supplier or just by specifying its class (3). The HttpHandler is created using WebHttpHandlerBuilder (4).

The HttpHandler can then be installed in one of the supported runtimes:

// Tomcat and Jetty (also see notes below)
HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
...

// Reactor Netty
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port).newHandler(adapter).onClose().block();

// RxNetty
RxNettyHttpHandlerAdapter adapter = new RxNettyHttpHandlerAdapter(handler);
HttpServer server = HttpServer.newServer(new InetSocketAddress(host, port));
server.startAndAwait(adapter);

// Undertow
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
[Note]Note

For Servlet containers especially with WAR deployment you can use the AbstractAnnotationConfigDispatcherHandlerInitializer which as a WebApplicationInitializer and is auto-detected by Servlet containers. It takes care of registering the ServletHttpHandlerAdapter as shown above. You will need to implement one abstract method in order to point to your Spring configuration.

23.3.3 Examples

You will find code examples useful to build reactive Web application in the following projects: