This section provides basic information on the Spring Web Reactive support in Spring Framework 5.
This section provides basic information on the reactive programming
support for Web applications in Spring Framework 5.
[[web-reactive-intro]]
@ -8,18 +9,20 @@ This section provides basic information on the Spring Web Reactive support in Sp
@@ -8,18 +9,20 @@ This section provides basic information on the Spring Web Reactive support in Sp
[[web-reactive-programming]]
=== Reactive Programming
=== 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. A key aspect of that
definition is the concept of backpressure which is a mechanism to ensure producers
don't overwhelm consumers. For example in a pipeline of reactive components that extends
from the database to the HTTP socket when the HTTP client is slow the data
repository slows down or stops until capacity frees up.
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).
From a programming model perspective reactive programming involves a major shift from imperative style logic
to a declarative composition of async logic. It is comparable to using `CompletableFuture` in Java 8
and composing follow-up actions via lambda expressions.
A key aspect of the 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 connection when the
HTTP client is slow the data repository also slows down or stops until capacity frees up.
Reactive programming also involves a major shift from imperative style logic
to declarative async composition of logic. It is comparable to using `CompletableFuture`
in Java 8 and composing follow-up actions via lambda expressions.
For a more extended introduction to reactive programming check the excellent multi-part series
https://spring.io/blog/2016/06/07/notes-on-reactive-programming-part-i-the-reactive-landscape["Notes on Reactive Programming"]
@ -27,7 +30,7 @@ by Dave Syer.
@@ -27,7 +30,7 @@ by Dave Syer.
* `Observable<Account> accounts` -- input streaming with RxJava.
Similarly, the response body can be in any one of the following ways:
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.
@ -212,11 +215,54 @@ Similarly, the response body can be in any one of the following ways:
@@ -212,11 +215,54 @@ Similarly, the response body can be in any one of the following ways:
* `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.
* `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.
[[web-reactive-websocket-support]]
=== 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@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:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
WebSocketClient client = new ReactorNettyWebSocketClient();
@ -226,18 +272,22 @@ when the method returns; implies a synchronous, non-blocking controller method.
@@ -226,18 +272,22 @@ when the method returns; implies a synchronous, non-blocking controller method.
The
https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[Spring Boot Web Reactive starter]
available via http://start.spring.io is the fastest way to get started if you want to use
the annotation-based programming model. It does all that's necessary so you can start
writing `@Controller` classes. By default it runs on Tomcat but the dependencies can
be changed as usual with Spring Boot to switch to a different runtime.
There is no Spring Boot Starter available yet for the functional programming model, so for
this one use the manual bootstraping method described bellow.
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.
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".
[[web-reactive-getting-started-manual]]
=== Manual Bootstrapping
This section outlines the steps to get up and running without Spring Boot.
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`
@ -250,7 +300,7 @@ Lastly add the dependencies for one of the supported runtimes:
@@ -250,7 +300,7 @@ Lastly add the dependencies for one of the supported runtimes:
* RxNetty -- `io.reactivex:rxnetty-common` and `io.reactivex:rxnetty-http`
* Undertow -- `io.undertow:undertow-core`
For the **annotation-based programming model**, bootstrap code start with:
For the **annotation-based programming model** bootstrap with:
The above loads default Spring Web Reactive config (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.
it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling.
For the **functional programming model**, bootstrap code start with:
For the **functional programming model** bootstrap as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
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)
@ -279,7 +330,7 @@ The above creates an `AnnotationConfigApplicationContext` instance (1) that can
@@ -279,7 +330,7 @@ The above creates an `AnnotationConfigApplicationContext` instance (1) that can
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).
**Common to both** annotation and functional flavors, an `HttpHandler` can then be installed in each supported runtime:
The `HttpHandler` can then be installed in one of the supported runtimes:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -290,10 +341,7 @@ HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
@@ -290,10 +341,7 @@ HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
// Reactor Netty
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port)
.newHandler(adapter)
.doOnNext(c -> System.out.println("Server listening on " + c.address())).block()