1
1
[[web-reactive]]
2
2
= Web Reactive Framework
3
- This section provides basic information on the Spring Web Reactive support in Spring Framework 5.
3
+ This section provides basic information on the reactive programming
4
+ support for Web applications in Spring Framework 5.
4
5
5
6
6
7
[[web-reactive-intro]]
7
8
== Introduction
8
9
9
10
10
11
[[web-reactive-programming]]
11
- === Reactive Programming
12
+ === What is Reactive Programming?
12
13
13
14
In plain terms reactive programming is about non-blocking applications that are asynchronous
14
- and event-driven and require a small number of threads to scale. A key aspect of that
15
- definition is the concept of backpressure which is a mechanism to ensure producers
16
- don't overwhelm consumers. For example in a pipeline of reactive components that extends
17
- from the database to the HTTP socket when the HTTP client is slow the data
18
- repository slows down or stops until capacity frees up.
15
+ and event-driven and require a small number of threads to scale vertically (i.e. within the
16
+ JVM) rather than horizontally (i.e. through clustering).
19
17
20
- From a programming model perspective reactive programming involves a major shift from imperative style logic
21
- to a declarative composition of async logic. It is comparable to using `CompletableFuture` in Java 8
22
- and composing follow-up actions via lambda expressions.
18
+ A key aspect of the reactive applications is the concept of backpressure which is
19
+ a mechanism to ensure producers don't overwhelm consumers. For example in a pipeline
20
+ of reactive components extending from the database to the HTTP connection when the
21
+ HTTP client is slow the data repository also slows down or stops until capacity frees up.
22
+
23
+ Reactive programming also involves a major shift from imperative style logic
24
+ to declarative async composition of logic. It is comparable to using `CompletableFuture`
25
+ in Java 8 and composing follow-up actions via lambda expressions.
23
26
24
27
For a more extended introduction to reactive programming check the excellent multi-part series
25
28
https://spring.io/blog/2016/06/07/notes-on-reactive-programming-part-i-the-reactive-landscape["Notes on Reactive Programming"]
26
29
by Dave Syer.
27
30
28
31
29
32
[[web-reactive-api]]
30
- === Spring Web Reactive Types
33
+ === Reactive API and Building Blocks
31
34
32
35
Spring Framework 5 embraces
33
36
https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams[Reactive Streams]
@@ -50,46 +53,44 @@ by Sebastien Deleuze.
50
53
[[web-reactive-feature-overview]]
51
54
== Spring Web Reactive Overview
52
55
53
- Spring Framework 5 adds a new `spring-web-reactive` module that provides both reactive
54
- client and server.
56
+ Spring Framework 5 includes a new `spring-web-reactive` module. The module contains support
57
+ for reactive HTTP and WebSocket clients as well as for reactive server web applications
58
+ including REST, HTML browser, and WebSocket style interactions.
55
59
56
60
[[web-reactive-server]]
57
- === Reactive Web Server
61
+ === Server-Side Reactive Web Support
58
62
59
- The reactive web server is available in 2 flavors :
63
+ On the server-side the new reactive module supports 2 distinct programming models :
60
64
61
- * With the same `@Controller` annotation-based programming model than Spring MVC
62
- * With a new functional programming model using Java 8 lambdas
65
+ * Annotation-based with `@Controller` and the other annotations supported also with Spring MVC
66
+ * Functional, Java 8 lambda style routing and handling
63
67
64
- When using Spring Web Reactive, regardless of the programming model you choose, your
65
- application is executed on a reactive non-blocking engine. The diagram below shows how
66
- Spring MVC and Spring Web Reactive compare side by side:
68
+ Both programming models are executed on the same reactive foundation that adapts
69
+ non-blocking HTTP runtimes to the Reactive Streams API. The diagram
70
+ below shows the server-side stack including traditional, Servlet-based
71
+ Spring MVC on the left from the `spring-web-mvc` module and also the
72
+ reactive stack on the right from the `spring-web-reactive` module.
67
73
68
74
image::images/web-reactive-overview.png[width=720]
69
75
70
- Spring Web Reactive makes use of Servlet 3.1 non-blocking I/O and runs on
71
- Servlet 3.1 containers. It also runs on non-Servlet runtimes such as Netty and Undertow.
72
- Each runtime is adapted to a set of shared, reactive `ServerHttpRequest` and
73
- `ServerHttpResponse` abstractions that expose the request and response body
74
- as `Flux<DataBuffer>` with full backpressure support on the read and the
75
- write side.
76
-
77
- JSON or XML REST webservices are supported, as well as view rendering, server-sent events
78
- and Websocket.
76
+ The new reactive stack can run on Servlet containers with support for the
77
+ Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as
78
+ Netty and Undertow. Each runtime is adapted to a reactive
79
+ `ServerHttpRequest` and `ServerHttpResponse` exposing the body of the
80
+ request and response as `Flux<DataBuffer>`, rather than
81
+ `InputStream` and `OutputStream`, with reactive backpressure.
82
+ REST-style JSON and XML serialization and deserialization is supported on top
83
+ as a `Flux<Object>`, and so is HTML view rendering and Server-Sent Events.
79
84
80
85
[[web-reactive-server-annotation]]
81
- ==== Annotation-based programming model
82
-
83
- The `@Controller` programming model supported by Spring Web Reactive re-defines many of
84
- the Spring MVC contracts such as `HandlerMapping` and `HandlerAdapter` to be asynchronous
85
- and non-blocking and to operate on the reactive HTTP request and response. For this reason
86
- Spring MVC and Spring Web Reactive cannot share any code. However they do share
87
- many of the same algorithms.
86
+ ==== Annotation-based Programming Model
88
87
89
- The end result is a programming model identical to today's Spring MVC but
90
- with support for reactive types and executed in a reactive manner.
91
-
92
- Here is an example of a reactive controller declared with annotations:
88
+ The same `@Controller` programming model and the same annotations used in Spring MVC
89
+ are also supported on the reactive side. The main difference is that the framework
90
+ contracts underneath -- i.e. `HandlerMapping`, `HandlerAdapter`, are
91
+ non-blocking and operate on the reactive `ServerHttpRequest` and `ServerHttpResponse`
92
+ rather than on the `HttpServletRequest` and `HttpServletResponse`.
93
+ Below is an example with a reactive controller:
93
94
94
95
[source,java,indent=0]
95
96
[subs="verbatim,quotes"]
@@ -121,14 +122,12 @@ public class PersonController {
121
122
----
122
123
123
124
[[web-reactive-server-functional]]
124
- ==== Functional programming model
125
-
126
- The functional programming model uses Java 8 lambdas instead of annotations to allow you
127
- to create a web application. It is built on top of simple but powerful building blocks like
128
- `RouterFunction` and `HandlerFunction`. For more details, see this
129
- https://spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework[blog post].
125
+ ==== Functional Programming Model
130
126
131
- Here is an example of a Spring web functional controller:
127
+ The functional programming model uses Java 8 lambda style routing and request
128
+ handling instead of annotations. The main API contracts are functional interfaces named
129
+ `RouterFunction` and `HandlerFunction`. They are simple but powerful building blocks
130
+ for creating web applications. Below is an example of functional request handling:
132
131
133
132
[source,java,indent=0]
134
133
[subs="verbatim,quotes"]
@@ -143,67 +142,71 @@ RouterFunctions
143
142
.then(person -> ServerResponse.ok().body(Mono.just(person), Person.class))
144
143
.otherwiseIfEmpty(notFound);
145
144
})
146
-
147
145
.andRoute(GET("/person").and(accept(APPLICATION_JSON)), request ->
148
146
ServerResponse.ok().body(repository.findAll(), Person.class))
149
-
150
147
.andRoute(POST("/person").and(contentType(APPLICATION_JSON)), request ->
151
148
ServerResponse.ok().build(repository.save(request.bodyToMono(Person.class))));
152
149
----
153
150
154
- [[web-reactive-client]]
155
- === Reactive Web Client
151
+ For more on the functional programming model see the
152
+ https://spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework[M3 release blog post].
156
153
157
- Spring Framework 5 adds a new reactive `WebClient` in addition to the existing `RestTemplate`
158
- and `AsyncRestTemplate`. In addition to a revised API, a big difference between
159
- `AsyncRestTemplate` and the reactive `WebClient` is that the later allows to consume
160
- streaming APIs like https://dev.twitter.com/streaming/overview[Twitter one] for example.
161
154
162
- A `WebClient` instance use a `ClientHttpConnector` implementation to drive the underlying
163
- supported HTTP client (e.g. Reactor Netty). This client is adapted to a set of shared,
164
- reactive `ClientHttpRequest` and `ClientHttpResponse` abstractions that expose the request
165
- and response body as `Flux<DataBuffer>` with full backpressure support on the read and
166
- the write side. The `HttpMessageReader` and `HttpMessageWriter` abstractions are also used on
167
- the client side for the serialization of a `Flux` of bytes to and from typed objects.
155
+ [[web-reactive-client]]
156
+ === Client-Side Reactive Web Support
168
157
169
- An example of using the `WebClient`:
158
+ Spring Framework 5 includes a functional, reactive `WebClient` that offers a fully
159
+ non-blocking and reactive alternative to the `RestTemplate`. It exposes network
160
+ input and output as a reactive `ClientHttpRequest` and `ClientHttpRespones` where
161
+ the body of the request and response is a `Flux<DataBuffer>` rather than an
162
+ `InputStream` and `OutputStream`. In addition it supports the same reactive JSON, XML,
163
+ and SSE serialization mechanism as on the server side so you can work with typed objects.
164
+ Below is an example of using the `WebClient` which requires a `ClientHttpConnector`
165
+ implementation to plug in a specific HTTP client such as Reactor Netty:
170
166
171
167
[source,java,indent=0]
172
168
[subs="verbatim,quotes"]
173
169
----
174
- // create an immutable instance of WebClient
175
- WebClient webClient = WebClient.create(new ReactorClientHttpConnector());
170
+ WebClient client = WebClient.create(new ReactorClientHttpConnector());
176
171
177
- ClientRequest<Void> request = ClientRequest.GET("http://example.com/accounts/{id}", 1L)
178
- .accept(MediaType.APPLICATION_JSON).build();
172
+ ClientRequest<Void> request = ClientRequest
173
+ .GET("http://example.com/accounts/{id}", 1L)
174
+ .accept(APPLICATION_JSON)
175
+ .build();
179
176
180
- Mono<Account> account = this.webClient
181
- .exchange(request)
182
- .then(response -> response.bodyToMono(Account.class));
177
+ Mono<Account> account = client
178
+ .exchange(request)
179
+ .then(response -> response.bodyToMono(Account.class));
183
180
----
184
181
185
- A `WebSocketClient` is also available.
182
+
183
+ [NOTE]
184
+ ====
185
+ The `AsyncRestTemplate` also supports non-blocking interactions. The main difference
186
+ is it can't support non-blocking streaming, like for example
187
+ https://dev.twitter.com/streaming/overview[Twitter one], because fundamentally it's
188
+ still based and relies on `InputStream` and `OutputStream`.
189
+ ====
190
+
186
191
187
192
[[web-reactive-http-body]]
188
- === Reading and writing HTTP body
193
+ === Encoding and Decoding the Request and Response Body
189
194
190
195
The `spring-core` module provides reactive `Encoder` and `Decoder` contracts
191
196
that enable the serialization of a `Flux` of bytes to and from typed objects.
192
197
The `spring-web` module adds JSON (Jackson) and XML (JAXB) implementations for use in
193
198
web applications as well as others for SSE streaming and zero-copy file transfer.
194
199
195
- Whether you use the annotation-based or functional programming model, the request body
196
- provided can be for example one of the following ways :
200
+ For example the request body can be one of the following way and it will be decoded
201
+ automatically in both the annotation and the functional programming models :
197
202
198
- * `Account account` -- the account is deserialized without
199
- blocking before the controller is invoked.
200
- * `Mono<Account> account` -- the controller can use the `Mono`
201
- to declare logic to be executed after the account is deserialized.
203
+ * `Account account` -- the account is deserialized without blocking before the controller is invoked.
204
+ * `Mono<Account> account` -- the controller can use the `Mono` to declare logic to be executed after the account is deserialized.
202
205
* `Single<Account> account` -- same as with `Mono` but using RxJava
203
206
* `Flux<Account> accounts` -- input streaming scenario.
204
207
* `Observable<Account> accounts` -- input streaming with RxJava.
205
208
206
- Similarly, the response body can be in any one of the following ways :
209
+ The response body can be one of the following:
207
210
208
211
* `Mono<Account>` -- serialize without blocking the given Account when the `Mono` completes.
209
212
* `Single<Account>` -- same but using RxJava.
@@ -212,11 +215,54 @@ Similarly, the response body can be in any one of the following ways:
212
215
* `Flowable<Account>` -- same but using RxJava 2 `Flowable` type.
213
216
* `Flux<ServerSentEvent>` -- SSE streaming.
214
217
* `Mono<Void>` -- request handling completes when the `Mono` completes.
215
- * `Account` -- serialize without blocking the given Account;
216
- implies a synchronous, non-blocking controller method.
218
+ * `Account` -- serialize without blocking the given Account; implies a synchronous, non-blocking controller method.
217
219
* `void` -- specific to the annotation-based programming model, request handling completes
218
220
when the method returns; implies a synchronous, non-blocking controller method.
219
221
222
+ [[web-reactive-websocket-support]]
223
+ === Reactive WebSocket Support
224
+
225
+ The Spring Framework 5 `spring-web-reactive` module includes reactive WebSocket
226
+ client and server support. Both client and server are supported on the Java WebSocket API
227
+ (JSR-356), Jetty, Undertow, Reactor Netty, and RxNetty.
228
+
229
+ On the server side, declare a `WebSocketHandlerAdapter` and then simply add
230
+ mappings to `WebSocketHandler`-based endpoints:
231
+
232
+ [source,java,indent=0]
233
+ [subs="verbatim,quotes"]
234
+ ----
235
+ @Bean
236
+ public HandlerMapping webSocketMapping() {
237
+ Map<String, WebSocketHandler> map = new HashMap<>();
238
+ map.put("/foo", new FooWebSocketHandler());
239
+ map.put("/bar", new BarWebSocketHandler());
240
+
241
+ SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
242
+ mapping.setUrlMap(map);
243
+ return mapping;
244
+ }
245
+
246
+ @Bean
247
+ public WebSocketHandlerAdapter handlerAdapter() {
248
+ return new WebSocketHandlerAdapter();
249
+ }
250
+ ----
251
+
252
+ On the client side create a `WebSocketClient` for one of the supported libraries
253
+ listed above:
254
+
255
+ [source,java,indent=0]
256
+ [subs="verbatim,quotes"]
257
+ ----
258
+ WebSocketClient client = new ReactorNettyWebSocketClient();
259
+ client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000);
260
+ ----
261
+
262
+
263
+
264
+
265
+
220
266
[[web-reactive-getting-started]]
221
267
== Getting Started
222
268
@@ -226,18 +272,22 @@ when the method returns; implies a synchronous, non-blocking controller method.
226
272
227
273
The
228
274
https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[Spring Boot Web Reactive starter]
229
- available via http://start.spring.io is the fastest way to get started if you want to use
230
- the annotation-based programming model. It does all that's necessary so you can start
231
- writing `@Controller` classes. By default it runs on Tomcat but the dependencies can
232
- be changed as usual with Spring Boot to switch to a different runtime.
233
-
234
- There is no Spring Boot Starter available yet for the functional programming model, so for
235
- this one use the manual bootstraping method described bellow.
275
+ available via http://start.spring.io is the fastest way to get started.
276
+ It does all that's necessary so you to start writing `@Controller` classes
277
+ just like with Spring MVC. Simply go to http://start.spring.io, choose
278
+ version 2.0.0.BUILD-SNAPSHOT, and type reactive in the dependencies box.
279
+ By default the starter runs with Tomcat but the dependencies can be changed as usual with Spring Boot to switch to a different runtime.
280
+ See the
281
+ https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[starter]
282
+ page for more details and instruction
283
+
284
+ There is no Spring Boot Starter for the functional programming model yet but
285
+ it's very easy to try it out. See the next section on "Manual Bootstrapping".
236
286
237
287
[[web-reactive-getting-started-manual]]
238
288
=== Manual Bootstrapping
239
289
240
- This section outlines the steps to get up and running without Spring Boot .
290
+ This section outlines the steps to get up and running manually .
241
291
242
292
For dependencies start with `spring-web-reactive` and `spring-context`.
243
293
Then add `jackson-databind` and `io.netty:netty-buffer`
@@ -250,7 +300,7 @@ Lastly add the dependencies for one of the supported runtimes:
250
300
* RxNetty -- `io.reactivex:rxnetty-common` and `io.reactivex:rxnetty-http`
251
301
* Undertow -- `io.undertow:undertow-core`
252
302
253
- For the **annotation-based programming model**, bootstrap code start with:
303
+ For the **annotation-based programming model** bootstrap with:
254
304
[source,java,indent=0]
255
305
[subs="verbatim,quotes"]
256
306
----
@@ -260,15 +310,16 @@ HttpHandler handler = DispatcherHandler.toHttpHandler(context); // (2)
260
310
261
311
The above loads default Spring Web Reactive config (1), then creates a
262
312
`DispatcherHandler`, the main class driving request processing (2), and adapts
263
- it to `HttpHandler`, the lowest level Spring abstraction for reactive HTTP request handling.
313
+ it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling.
264
314
265
- For the **functional programming model**, bootstrap code start with :
315
+ For the **functional programming model** bootstrap as follows :
266
316
[source,java,indent=0]
267
317
[subs="verbatim,quotes"]
268
318
----
269
319
ApplicationContext context = new AnnotationConfigApplicationContext(); // (1)
270
320
context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2)
271
321
context.registerBean(BarBean.class); // (3)
322
+
272
323
HttpHandler handler = WebHttpHandlerBuilder
273
324
.webHandler(RouterFunctions.toHttpHandler(...))
274
325
.applicationContext(context)
@@ -279,7 +330,7 @@ The above creates an `AnnotationConfigApplicationContext` instance (1) that can
279
330
of the new functional bean registration API (2) to register beans using a Java 8 `Supplier`
280
331
or just by specifying its class (3). The `HttpHandler` is created using `WebHttpHandlerBuilder` (4).
281
332
282
- **Common to both** annotation and functional flavors, an `HttpHandler` can then be installed in each supported runtime :
333
+ The `HttpHandler` can then be installed in one of the supported runtimes :
283
334
284
335
[source,java,indent=0]
285
336
[subs="verbatim,quotes"]
@@ -290,10 +341,7 @@ HttpServlet servlet = new ServletHttpHandlerAdapter(handler);
290
341
291
342
// Reactor Netty
292
343
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
293
- HttpServer.create(host, port)
294
- .newHandler(adapter)
295
- .doOnNext(c -> System.out.println("Server listening on " + c.address())).block()
296
- .onClose().block();
344
+ HttpServer.create(host, port).newHandler(adapter).onClose().block();
297
345
298
346
// RxNetty
299
347
RxNettyHttpHandlerAdapter adapter = new RxNettyHttpHandlerAdapter(handler);
@@ -308,10 +356,12 @@ server.start();
308
356
309
357
[NOTE]
310
358
====
311
- For Servlet runtimes you can use the `AbstractAnnotationConfigDispatcherHandlerInitializer`,
312
- which as a `WebApplicationInitializer` is auto-detected by Servlet containers
313
- and it registers for you the `ServletHttpHandlerAdapter` shown above.
314
- Only implement one method to point to your Spring Java configuration classes.
359
+ For Servlet containers especially with WAR deployment you can use the
360
+ `AbstractAnnotationConfigDispatcherHandlerInitializer` which as a
361
+ `WebApplicationInitializer` and is auto-detected by Servlet containers.
362
+ It takes care of registering the `ServletHttpHandlerAdapter` as shown above.
363
+ You will need to implement one abstract method in order to point to your
364
+ Spring configuration.
315
365
====
316
366
317
367
[[web-reactive-getting-started-examples]]
0 commit comments