Update Spring Web Reactive reference documentation

Issue: SPR-14912
This commit is contained in:
Sebastien Deleuze 2016-12-28 18:20:15 +01:00
parent 209e7a700d
commit 963ea062e4
2 changed files with 158 additions and 52 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -50,15 +50,20 @@ 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.
[[web-reactive-module]]
=== Spring Web Reactive Module
[[web-reactive-server]]
=== Reactive Web Server
The reactive web server is available in 2 flavors:
Spring Framework 5 adds a new `spring-web-reactive` module that supports the same
`@Controller` programming model as Spring MVC but executed on a reactive,
non-blocking engine. The diagram below shows how Spring MVC and Spring Web
Reactive compare side by side:
* With the same `@Controller` annotation-based programming model than Spring MVC
* With a new functional programming model using Java 8 lambdas
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:
image::images/web-reactive-overview.png[width=720]
@ -69,51 +74,90 @@ Each runtime is adapted to a set of shared, reactive `ServerHttpRequest` and
as `Flux<DataBuffer>` with full backpressure support on the read and the
write side.
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.
JSON or XML REST webservices are supported, as well as view rendering, server-sent events
and Websocket.
The `spring-web-reactive` module contains the Spring Web Reactive framework that supports
the `@Controller` programming model. It 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
[[web-reactive-server-annotation]]
==== 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.
For example a controller method can declare a `@RequestBody` method argument
in any one of the following ways:
* `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.
Here is an example of a reactive controller declared with annotations:
Similarly a controller can also an `@ResponseBody` return value
in any one of the following ways:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@RestController
public class PersonController {
* `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.
* `void` -- request handling completes when the method returns;
implies a synchronous, non-blocking controller method.
* `Account` -- serialize without blocking the given Account;
implies a synchronous, non-blocking controller method.
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);
}
}
----
[[web-reactive-server-functional]]
==== 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
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))));
----
[[web-reactive-client]]
=== Reactive Web Client
Spring Framework 5 adds a new reactive `WebClient` in addition to the existing `RestTemplate`.
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,
@ -135,9 +179,44 @@ ClientRequest<Void> request = ClientRequest.GET("http://example.com/accounts/{id
Mono<Account> account = this.webClient
.exchange(request)
.then(response -> response.body(toMono(Account.class)));
.then(response -> response.bodyToMono(Account.class));
----
A `WebSocketClient` is also available.
[[web-reactive-http-body]]
=== Reading and writing HTTP 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:
* `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:
* `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.
[[web-reactive-getting-started]]
== Getting Started
@ -147,11 +226,13 @@ Mono<Account> account = this.webClient
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. It does all that's necessary so you can start
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.
[[web-reactive-getting-started-manual]]
=== Manual Bootstrapping
@ -159,7 +240,7 @@ be changed as usual with Spring Boot to switch to a different runtime.
This section outlines the steps to get up and running without Spring Boot.
For dependencies start with `spring-web-reactive` and `spring-context`.
Then add `jackson-databind` and `io.netty:netty-buffer:4.1.3.Final`
Then add `jackson-databind` and `io.netty:netty-buffer`
(temporarily see https://jira.spring.io/browse/SPR-14528[SPR-14528]) for JSON support.
Lastly add the dependencies for one of the supported runtimes:
@ -169,7 +250,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 bootstrap code start with:
For the **annotation-based programming model**, bootstrap code start with:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ -180,7 +261,25 @@ 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.
An `HttpHandler` can then be installed in each supported runtime:
For the **functional programming model**, bootstrap code start with:
[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)
.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).
**Common to both** annotation and functional flavors, an `HttpHandler` can then be installed in each supported runtime:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -191,8 +290,10 @@ HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
// Reactor Netty
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer server = HttpServer.create(host, port);
server.startAndAwait(adapter);
HttpServer.create(host, port)
.newHandler(adapter)
.doOnNext(c -> System.out.println("Server listening on " + c.address())).block()
.onClose().block();
// RxNetty
RxNettyHttpHandlerAdapter adapter = new RxNettyHttpHandlerAdapter(handler);
@ -213,12 +314,17 @@ and it registers for you the `ServletHttpHandlerAdapter` shown above.
Only implement one method to point to your Spring Java configuration classes.
====
[[web-reactive-getting-started-examples]]
=== Examples
You will find code examples useful to build your own Spring Web Reactive application in these projects:
[[web-reactive-getting-started-M1]]
=== Extent of Support in 5.0 M1
For M1 the Spring Web Reactive module focuses on REST scenarios for both
client and server. Basic HTML rendering with Freemarker is also supported but
limited to rendering but not form submissions.
* https://github.com/bclozel/spring-boot-web-reactive[Spring Boot Web Reactive Starter]: sources of the reactive starter available at http://start.spring.io
* https://github.com/poutsma/web-function-sample[Functional programming model sample]
* https://github.com/sdeleuze/spring-reactive-playground[Spring Reactive Playground]: plaground for most Spring Web Reactive features
* https://github.com/reactor/projectreactor.io/tree/spring-functional[Reactor website]: the `spring-functional` branch is a Spring Web Reactive functional web application
* https://github.com/bclozel/spring-reactive-university[Spring Reactive University]: live-coded project from https://www.youtube.com/watch?v=Cj4foJzPF80[this Devoxx BE 2106 university talk]
* https://github.com/thymeleaf/thymeleafsandbox-biglist-reactive[Reactive Thymeleaf Sandbox]
* https://github.com/mix-it/mixit/[Mix-it 2017 website]: Kotlin + Reactive + Functional web and bean registration API application
* https://github.com/simonbasle/reactor-by-example[Reactor by example]: code snippets coming from this https://www.infoq.com/articles/reactor-by-example[InfoQ article]
* https://github.com/spring-projects/spring-framework/tree/master/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation[Spring Web Reactive integration tests]: various features tested with Reactor https://projectreactor.io/docs/test/release/api/index.html?reactor/test/StepVerifier.html[`StepVerifier`]