More documentation updates
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
[[web-reactive]]
|
||||
= Web Reactive Framework
|
||||
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
|
||||
|
||||
|
||||
[[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.
|
||||
|
||||
|
||||
[[web-reactive-api]]
|
||||
=== Spring Web Reactive Types
|
||||
=== Reactive API and Building Blocks
|
||||
|
||||
Spring Framework 5 embraces
|
||||
https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams[Reactive Streams]
|
||||
@@ -50,46 +53,44 @@ by Sebastien Deleuze.
|
||||
[[web-reactive-feature-overview]]
|
||||
== Spring Web Reactive Overview
|
||||
|
||||
Spring Framework 5 adds a new `spring-web-reactive` module that provides both reactive
|
||||
client and server.
|
||||
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.
|
||||
|
||||
[[web-reactive-server]]
|
||||
=== Reactive Web Server
|
||||
=== Server-Side Reactive Web Support
|
||||
|
||||
The reactive web server is available in 2 flavors:
|
||||
On the server-side the new reactive module supports 2 distinct programming models:
|
||||
|
||||
* With the same `@Controller` annotation-based programming model than Spring MVC
|
||||
* With a new functional programming model using Java 8 lambdas
|
||||
* Annotation-based with `@Controller` and the other annotations supported also with Spring MVC
|
||||
* Functional, Java 8 lambda style routing and handling
|
||||
|
||||
When using Spring Web Reactive, regardless of the programming model you choose, your
|
||||
application is executed on a reactive non-blocking engine. The diagram below shows how
|
||||
Spring MVC and Spring Web Reactive compare side by side:
|
||||
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.
|
||||
|
||||
image::images/web-reactive-overview.png[width=720]
|
||||
|
||||
Spring Web Reactive makes use of Servlet 3.1 non-blocking I/O and runs on
|
||||
Servlet 3.1 containers. It also runs on non-Servlet runtimes such as Netty and Undertow.
|
||||
Each runtime is adapted to a set of shared, reactive `ServerHttpRequest` and
|
||||
`ServerHttpResponse` abstractions that expose the request and response body
|
||||
as `Flux<DataBuffer>` with full backpressure support on the read and the
|
||||
write side.
|
||||
|
||||
JSON or XML REST webservices are supported, as well as view rendering, server-sent events
|
||||
and Websocket.
|
||||
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.
|
||||
|
||||
[[web-reactive-server-annotation]]
|
||||
==== Annotation-based programming model
|
||||
==== Annotation-based Programming Model
|
||||
|
||||
The `@Controller` programming model supported by Spring Web Reactive re-defines many of
|
||||
the Spring MVC contracts such as `HandlerMapping` and `HandlerAdapter` to be asynchronous
|
||||
and non-blocking and to operate on the reactive HTTP request and response. For this reason
|
||||
Spring MVC and Spring Web Reactive cannot share any code. However they do share
|
||||
many of the same algorithms.
|
||||
|
||||
The end result is a programming model identical to today's Spring MVC but
|
||||
with support for reactive types and executed in a reactive manner.
|
||||
|
||||
Here is an example of a reactive controller declared with annotations:
|
||||
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:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
@@ -121,14 +122,12 @@ public class PersonController {
|
||||
----
|
||||
|
||||
[[web-reactive-server-functional]]
|
||||
==== Functional programming model
|
||||
==== Functional Programming Model
|
||||
|
||||
The functional programming model uses Java 8 lambdas instead of annotations to allow you
|
||||
to create a web application. It is built on top of simple but powerful building blocks like
|
||||
`RouterFunction` and `HandlerFunction`. For more details, see this
|
||||
https://spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework[blog post].
|
||||
|
||||
Here is an example of a Spring web functional controller:
|
||||
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:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
@@ -143,67 +142,71 @@ RouterFunctions
|
||||
.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
|
||||
https://spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework[M3 release blog post].
|
||||
|
||||
|
||||
[[web-reactive-client]]
|
||||
=== Reactive Web Client
|
||||
=== Client-Side Reactive Web Support
|
||||
|
||||
Spring Framework 5 adds a new reactive `WebClient` in addition to the existing `RestTemplate`
|
||||
and `AsyncRestTemplate`. In addition to a revised API, a big difference between
|
||||
`AsyncRestTemplate` and the reactive `WebClient` is that the later allows to consume
|
||||
streaming APIs like https://dev.twitter.com/streaming/overview[Twitter one] for example.
|
||||
|
||||
A `WebClient` instance use a `ClientHttpConnector` implementation to drive the underlying
|
||||
supported HTTP client (e.g. Reactor Netty). This client is adapted to a set of shared,
|
||||
reactive `ClientHttpRequest` and `ClientHttpResponse` abstractions that expose the request
|
||||
and response body as `Flux<DataBuffer>` with full backpressure support on the read and
|
||||
the write side. The `HttpMessageReader` and `HttpMessageWriter` abstractions are also used on
|
||||
the client side for the serialization of a `Flux` of bytes to and from typed objects.
|
||||
|
||||
An example of using the `WebClient`:
|
||||
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:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// create an immutable instance of WebClient
|
||||
WebClient webClient = WebClient.create(new ReactorClientHttpConnector());
|
||||
WebClient client = WebClient.create(new ReactorClientHttpConnector());
|
||||
|
||||
ClientRequest<Void> request = ClientRequest.GET("http://example.com/accounts/{id}", 1L)
|
||||
.accept(MediaType.APPLICATION_JSON).build();
|
||||
ClientRequest<Void> request = ClientRequest
|
||||
.GET("http://example.com/accounts/{id}", 1L)
|
||||
.accept(APPLICATION_JSON)
|
||||
.build();
|
||||
|
||||
Mono<Account> account = this.webClient
|
||||
.exchange(request)
|
||||
.then(response -> response.bodyToMono(Account.class));
|
||||
Mono<Account> account = client
|
||||
.exchange(request)
|
||||
.then(response -> response.bodyToMono(Account.class));
|
||||
----
|
||||
|
||||
A `WebSocketClient` is also available.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The `AsyncRestTemplate` also supports non-blocking interactions. The main difference
|
||||
is it can't support non-blocking streaming, like for example
|
||||
https://dev.twitter.com/streaming/overview[Twitter one], because fundamentally it's
|
||||
still based and relies on `InputStream` and `OutputStream`.
|
||||
====
|
||||
|
||||
|
||||
[[web-reactive-http-body]]
|
||||
=== Reading and writing HTTP body
|
||||
=== Encoding and Decoding the Request and Response Body
|
||||
|
||||
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.
|
||||
|
||||
Whether you use the annotation-based or functional programming model, the request body
|
||||
provided can be for example one of the following ways:
|
||||
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.
|
||||
* `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.
|
||||
|
||||
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:
|
||||
* `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();
|
||||
client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000);
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[[web-reactive-getting-started]]
|
||||
== Getting Started
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[starter]
|
||||
page for more details and instruction
|
||||
|
||||
There is no Spring Boot Starter available yet for the functional programming model, so for
|
||||
this one use the manual bootstraping method described bellow.
|
||||
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:
|
||||
* 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:
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -260,15 +310,16 @@ HttpHandler handler = DispatcherHandler.toHttpHandler(context); // (2)
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
// Reactor Netty
|
||||
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
|
||||
HttpServer.create(host, port)
|
||||
.newHandler(adapter)
|
||||
.doOnNext(c -> System.out.println("Server listening on " + c.address())).block()
|
||||
.onClose().block();
|
||||
HttpServer.create(host, port).newHandler(adapter).onClose().block();
|
||||
|
||||
// RxNetty
|
||||
RxNettyHttpHandlerAdapter adapter = new RxNettyHttpHandlerAdapter(handler);
|
||||
@@ -308,10 +356,12 @@ server.start();
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For Servlet runtimes you can use the `AbstractAnnotationConfigDispatcherHandlerInitializer`,
|
||||
which as a `WebApplicationInitializer` is auto-detected by Servlet containers
|
||||
and it registers for you the `ServletHttpHandlerAdapter` shown above.
|
||||
Only implement one method to point to your Spring Java configuration classes.
|
||||
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.
|
||||
====
|
||||
|
||||
[[web-reactive-getting-started-examples]]
|
||||
|
||||
Reference in New Issue
Block a user