webflux.adoc 14.3 KB
Newer Older
1
[[webflux]]
2
= Spring WebFlux
3

4
Spring Framework 5 includes a new `spring-webflux` module. The module contains support
R
Rossen Stoyanchev 已提交
5 6
for reactive HTTP and WebSocket clients as well as for reactive server web applications
including REST, HTML browser, and WebSocket style interactions.
7

8
[[webflux-server]]
9
== Server Side
10

11
On the server-side WebFlux supports 2 distinct programming models:
12

R
Rossen Stoyanchev 已提交
13 14
* Annotation-based with `@Controller` and the other annotations supported also with Spring MVC
* Functional, Java 8 lambda style routing and handling
15

R
Rossen Stoyanchev 已提交
16 17 18
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
19 20
Spring MVC on the left from the `spring-webmvc` module and also the
reactive stack on the right from the `spring-webflux` module.
21

22
image::images/webflux-overview.png[width=720]
23

24
WebFlux can run on Servlet containers with support for the
R
Rossen Stoyanchev 已提交
25 26 27 28 29 30 31
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.
32

33
[[webflux-server-annotation]]
34
=== Annotation-based Programming Model
35

R
Rossen Stoyanchev 已提交
36
The same `@Controller` programming model and the same annotations used in Spring MVC
37 38
are also supported in WebFlux. The main difference is that the underlying core,
framework contracts -- i.e. `HandlerMapping`, `HandlerAdapter`, are
R
Rossen Stoyanchev 已提交
39 40 41
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:
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
[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);
	}
}
----
71

72
include::webflux-functional.adoc[leveloffset=+2]
73

74

75
[[webflux-client]]
76
== Client Side
77

78
WebFlux includes a functional, reactive `WebClient` that offers a fully
R
Rossen Stoyanchev 已提交
79
non-blocking and reactive alternative to the `RestTemplate`. It exposes network
80
input and output as a reactive `ClientHttpRequest` and `ClientHttpResponse` where
R
Rossen Stoyanchev 已提交
81 82 83 84 85
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:
86 87 88 89

[source,java,indent=0]
[subs="verbatim,quotes"]
----
90
WebClient client = WebClient.create("http://example.com");
91

92 93
Mono<Account> account = client.get()
		.url("/accounts/{id}", 1L)
R
Rossen Stoyanchev 已提交
94 95
		.accept(APPLICATION_JSON)
		.exchange(request)
96
		.flatMap(response -> response.bodyToMono(Account.class));
97 98
----

99 100 101
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`:
R
Rossen Stoyanchev 已提交
102

103 104 105 106 107 108 109 110 111 112 113
[source,java,indent=0]
[subs="verbatim,quotes"]
----
SslContext sslContext = ...
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(builder -> {
	builder.sslContext(sslContext);
});
WebClient webClient = WebClient.builder()
		.clientConnector(connector)
		.build();
----
114

115
[[webflux-http-body]]
116
== Request and Response Body Conversion
117 118 119 120 121 122

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.

123 124 125 126 127 128
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

R
Rossen Stoyanchev 已提交
129 130
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:
131

R
Rossen Stoyanchev 已提交
132 133
* `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.
134 135 136 137
* `Single<Account> account` -- same as with `Mono` but using RxJava
* `Flux<Account> accounts` -- input streaming scenario.
* `Observable<Account> accounts` -- input streaming with RxJava.

R
Rossen Stoyanchev 已提交
138
The response body can be one of the following:
139 140 141 142 143 144

* `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.
145
* `Publisher<Account>` or `Flow.Publisher<Account>` -- any type implementing Reactive Streams `Publisher` is supported.
146 147
* `Flux<ServerSentEvent>` -- SSE streaming.
* `Mono<Void>` -- request handling completes when the `Mono` completes.
R
Rossen Stoyanchev 已提交
148
* `Account` -- serialize without blocking the given Account; implies a synchronous, non-blocking controller method.
149 150 151
* `void` -- specific to the annotation-based programming model, request handling completes
when the method returns; implies a synchronous, non-blocking controller method.

152 153
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
154
and flushed. For example a REST endpoint that returns a `Flux<Account>` will be serialized by
155 156
default as following:

157
* `application/json`: a `Flux<Account>` is handled as an asynchronous collection and
158
  serialized as a JSON array with an explicit flush when the `complete` event is emitted.
159
* `application/stream+json`: a `Flux<Account>` will be handled as a stream of `Account` elements
160 161 162
  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.
163 164
* `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
165 166 167 168 169
  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.


170
[[webflux-websocket-support]]
171
== Reactive WebSocket Support
R
Rossen Stoyanchev 已提交
172

173 174
WebFlux includes reactive WebSocket client and server support.
Both client and server are supported on the Java WebSocket API
175
(JSR-356), Jetty, Undertow, and Reactor Netty.
R
Rossen Stoyanchev 已提交
176 177 178 179 180 181 182 183 184 185 186 187 188 189

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();
190
	mapping.setOrder(10);
R
Rossen Stoyanchev 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	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);
----

211
[[webflux-tests]]
212
== Testing
R
Rossen Stoyanchev 已提交
213

214 215 216 217 218 219 220 221 222 223 224
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.
R
Rossen Stoyanchev 已提交
225 226 227



228
[[webflux-getting-started]]
229
= Getting Started
230 231


232
[[webflux-getting-started-boot]]
233
== Spring Boot Starter
234

235
The
236
Spring Boot WebFlux starter available via http://start.spring.io is the fastest way to get started.
R
Rossen Stoyanchev 已提交
237 238 239
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.
240 241 242
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.
R
Rossen Stoyanchev 已提交
243

244
[[webflux-getting-started-manual]]
245
== Manual Bootstrapping
246

R
Rossen Stoyanchev 已提交
247
This section outlines the steps to get up and running manually.
248

249
For dependencies start with `spring-webflux` and `spring-context`.
250
Then add `jackson-databind` and `io.netty:netty-buffer`
251 252 253 254 255 256 257 258
(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`

R
Rossen Stoyanchev 已提交
259
For the **annotation-based programming model** bootstrap with:
260 261 262
[source,java,indent=0]
[subs="verbatim,quotes"]
----
263
ApplicationContext context = new AnnotationConfigApplicationContext(DelegatingWebFluxConfiguration.class);  // (1)
J
Juergen Hoeller 已提交
264
HttpHandler handler = DispatcherHandler.toHttpHandler(context);  // (2)
265 266
----

R
Rossen Stoyanchev 已提交
267
The above loads default Spring Web framework configuration (1), then creates a
268
`DispatcherHandler`, the main class driving request processing (2), and adapts
R
Rossen Stoyanchev 已提交
269
it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling.
270

R
Rossen Stoyanchev 已提交
271
For the **functional programming model** bootstrap as follows:
272 273 274
[source,java,indent=0]
[subs="verbatim,quotes"]
----
275
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // (1)
276 277
context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2)
context.registerBean(BarBean.class); // (3)
278
context.refresh();
R
Rossen Stoyanchev 已提交
279

280 281 282 283 284 285 286 287 288 289
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).

R
Rossen Stoyanchev 已提交
290
The `HttpHandler` can then be installed in one of the supported runtimes:
291

292 293 294
[source,java,indent=0]
[subs="verbatim,quotes"]
----
295
// Tomcat and Jetty (also see notes below)
J
Juergen Hoeller 已提交
296
HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
297 298 299
...

// Reactor Netty
J
Juergen Hoeller 已提交
300
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
R
Rossen Stoyanchev 已提交
301
HttpServer.create(host, port).newHandler(adapter).block();
302 303

// Undertow
J
Juergen Hoeller 已提交
304 305
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
R
Rossen Stoyanchev 已提交
306
server.start();
307
----
308

309 310
[NOTE]
====
R
Rossen Stoyanchev 已提交
311 312 313 314 315 316
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.
317 318
====

319
[[webflux-getting-started-examples]]
320
== Examples
321

R
Rossen Stoyanchev 已提交
322
You will find code examples useful to build reactive Web application in the following projects:
323

324
* https://github.com/poutsma/web-function-sample[Functional programming model sample]
R
Rossen Stoyanchev 已提交
325 326 327
* 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]
328
* https://github.com/thymeleaf/thymeleafsandbox-biglist-reactive[Reactive Thymeleaf Sandbox]
S
Sebastien Deleuze 已提交
329 330
* 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
331
* 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]
332
* 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`]