spring-framework/src/docs/asciidoc/web/webflux.adoc

333 lines
14 KiB
Plaintext
Raw Normal View History

[[webflux]]
= Spring WebFlux
Spring Framework 5 includes a new `spring-webflux` module. The module contains support
2016-12-30 01:35:01 +08:00
for reactive HTTP and WebSocket clients as well as for reactive server web applications
including REST, HTML browser, and WebSocket style interactions.
[[webflux-server]]
== Server Side
On the server-side WebFlux supports 2 distinct programming models:
2016-12-30 01:35:01 +08:00
* Annotation-based with `@Controller` and the other annotations supported also with Spring MVC
* Functional, Java 8 lambda style routing and handling
2016-12-30 01:35:01 +08:00
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-webmvc` module and also the
reactive stack on the right from the `spring-webflux` module.
image::images/webflux-overview.png[width=720]
WebFlux can run on Servlet containers with support for the
2016-12-30 01:35:01 +08:00
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.
[[webflux-server-annotation]]
=== Annotation-based Programming Model
2016-12-30 01:35:01 +08:00
The same `@Controller` programming model and the same annotations used in Spring MVC
are also supported in WebFlux. The main difference is that the underlying core,
framework contracts -- i.e. `HandlerMapping`, `HandlerAdapter`, are
2016-12-30 01:35:01 +08:00
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:
2016-07-26 22:23:12 +08:00
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@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);
}
}
----
2016-07-26 22:23:12 +08:00
include::webflux-functional.adoc[leveloffset=+2]
[[webflux-client]]
== Client Side
WebFlux includes a functional, reactive `WebClient` that offers a fully
2016-12-30 01:35:01 +08:00
non-blocking and reactive alternative to the `RestTemplate`. It exposes network
input and output as a reactive `ClientHttpRequest` and `ClientHttpResponse` where
2016-12-30 01:35:01 +08:00
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"]
----
WebClient client = WebClient.create("http://example.com");
Mono<Account> account = client.get()
.url("/accounts/{id}", 1L)
2016-12-30 01:35:01 +08:00
.accept(APPLICATION_JSON)
.exchange(request)
.flatMap(response -> response.bodyToMono(Account.class));
----
As stated above, `WebClient` requires a `ClientHttpConnector` to operate, the default being the `ReactorClientHttpConnector`.
When constructing a `ReactorClientHttpConnector`, you can use the `HttpClientOptions.Builder` to further customize it.
For instance, to customize the Netty `SslContext`:
2016-12-30 01:35:01 +08:00
[source,java,indent=0]
[subs="verbatim,quotes"]
----
SslContext sslContext = ...
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(builder -> {
builder.sslContext(sslContext);
});
WebClient webClient = WebClient.builder()
.clientConnector(connector)
.build();
----
[[webflux-http-body]]
== 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.
2017-03-17 04:51:35 +08:00
The following Reactive APIs are supported:
* Reactor 3.x is supported out of the box
* RxJava 2.x is supported when `io.reactivex.rxjava2:rxjava` dependency is on the classpath
* RxJava 1.x is supported when both `io.reactivex:rxjava` and `io.reactivex:rxjava-reactive-streams` (https://github.com/ReactiveX/RxJavaReactiveStreams[adapter between RxJava and Reactive Streams]) dependencies are on the classpath
2016-12-30 01:35:01 +08:00
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:
2016-12-30 01:35:01 +08:00
* `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.
2016-12-30 01:35:01 +08:00
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.
2017-03-17 04:51:35 +08:00
* `Publisher<Account>` or `Flow.Publisher<Account>` -- any type implementing Reactive Streams `Publisher` is supported.
* `Flux<ServerSentEvent>` -- SSE streaming.
* `Mono<Void>` -- request handling completes when the `Mono` completes.
2016-12-30 01:35:01 +08:00
* `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.
When using stream types like `Flux` or `Observable`, the media type specified in the
request/response or at mapping/routing level is used to determine how the data should be serialized
2017-03-17 04:51:35 +08:00
and flushed. For example a REST endpoint that returns a `Flux<Account>` will be serialized by
default as following:
2017-03-17 04:51:35 +08:00
* `application/json`: a `Flux<Account>` is handled as an asynchronous collection and
serialized as a JSON array with an explicit flush when the `complete` event is emitted.
2017-03-17 04:51:35 +08:00
* `application/stream+json`: a `Flux<Account>` will be handled as a stream of `Account` elements
serialized as individual JSON object separated by new lines and explicitly flushed after
each element. The `WebClient` supports JSON stream decoding so this is a good use case
for server to server use case.
2017-03-17 04:51:35 +08:00
* `text/event-stream`: a `Flux<Account>` or `Flux<ServerSentEvent<Account>>` will be handled as
a stream of `Account` or `ServerSentEvent` elements serialized as individual SSE elements
using by default JSON for data encoding and explicit flush after each element. This
is well suited for exposing a stream to browser clients. `WebClient` supports
reading SSE streams as well.
[[webflux-websocket-support]]
== Reactive WebSocket Support
2016-12-30 01:35:01 +08:00
WebFlux includes reactive WebSocket client and server support.
Both client and server are supported on the Java WebSocket API
(JSR-356), Jetty, Undertow, and Reactor Netty.
2016-12-30 01:35:01 +08:00
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.setOrder(10);
2016-12-30 01:35:01 +08:00
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);
----
[[webflux-tests]]
== Testing
2016-12-30 01:35:01 +08:00
2017-02-14 04:42:17 +08:00
The `spring-test` module includes a `WebTestClient` that can be used to test
WebFlux server endpoints with or without a running server.
Tests without a running server are comparable to `MockMvc` from Spring MVC
where mock request and response are used instead of connecting over the network
using a socket. The `WebTestClient` however can also perform tests against a
running server.
For more see
https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples[sample tests]
in the framework.
2016-12-30 01:35:01 +08:00
[[webflux-getting-started]]
= Getting Started
[[webflux-getting-started-boot]]
== Spring Boot Starter
The
Spring Boot WebFlux starter available via http://start.spring.io is the fastest way to get started.
2016-12-30 01:35:01 +08:00
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 Reactor Netty but the dependencies can be changed as usual
with Spring Boot to switch to a different runtime.
See the Spring Boot reference documentation page for more details and instruction.
2016-12-30 01:35:01 +08:00
[[webflux-getting-started-manual]]
== Manual Bootstrapping
2016-12-30 01:35:01 +08:00
This section outlines the steps to get up and running manually.
For dependencies start with `spring-webflux` and `spring-context`.
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:
* 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`
* Undertow -- `io.undertow:undertow-core`
2016-12-30 01:35:01 +08:00
For the **annotation-based programming model** bootstrap with:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
ApplicationContext context = new AnnotationConfigApplicationContext(DelegatingWebFluxConfiguration.class); // (1)
2016-09-21 17:24:20 +08:00
HttpHandler handler = DispatcherHandler.toHttpHandler(context); // (2)
----
2016-12-30 02:07:36 +08:00
The above loads default Spring Web framework configuration (1), then creates a
`DispatcherHandler`, the main class driving request processing (2), and adapts
2016-12-30 01:35:01 +08:00
it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling.
2016-12-30 01:35:01 +08:00
For the **functional programming model** bootstrap as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
2017-05-07 23:52:22 +08:00
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // (1)
context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2)
context.registerBean(BarBean.class); // (3)
2017-05-07 23:52:22 +08:00
context.refresh();
2016-12-30 01:35:01 +08:00
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).
2016-12-30 01:35:01 +08:00
The `HttpHandler` can then be installed in one of the supported runtimes:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
// Tomcat and Jetty (also see notes below)
2016-09-21 17:24:20 +08:00
HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
...
// Reactor Netty
2016-09-21 17:24:20 +08:00
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
2017-03-08 09:37:52 +08:00
HttpServer.create(host, port).newHandler(adapter).block();
// Undertow
2016-09-21 17:24:20 +08:00
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
2016-09-02 21:25:56 +08:00
server.start();
----
[NOTE]
====
2016-12-30 01:35:01 +08:00
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.
====
[[webflux-getting-started-examples]]
== Examples
2016-12-30 02:07:36 +08:00
You will find code examples useful to build reactive Web application in the following projects:
* https://github.com/poutsma/web-function-sample[Functional programming model sample]
2016-12-30 02:07:36 +08:00
* https://github.com/sdeleuze/spring-reactive-playground[Spring Reactive Playground]: playground for most Spring Web reactive features
* https://github.com/reactor/projectreactor.io/tree/spring-functional[Reactor website]: the `spring-functional` branch is a Spring 5 functional, Java 8 lambda-style application
* https://github.com/bclozel/spring-reactive-university[Spring Reactive University session]: 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]
2017-08-23 19:59:46 +08:00
* https://github.com/mixitconf/mixit/[MiXiT website]: Boot + Kotlin + Reactive + Functional web registration API application
* https://github.com/sdeleuze/spring-kotlin-functional[Spring Kotlin Functional]: Standalone WebFlux application with functional web and bean DSLs
* 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-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation[Spring integration tests]: various features tested with Reactor https://projectreactor.io/docs/test/release/api/index.html?reactor/test/StepVerifier.html[`StepVerifier`]