diff --git a/src/asciidoc/images/web-reactive-overview.png b/src/asciidoc/images/web-reactive-overview.png index 65538cf8c41..6371d3fea1c 100644 Binary files a/src/asciidoc/images/web-reactive-overview.png and b/src/asciidoc/images/web-reactive-overview.png differ diff --git a/src/asciidoc/web-reactive.adoc b/src/asciidoc/web-reactive.adoc index 6b496b8c10c..29ef1677eac 100644 --- a/src/asciidoc/web-reactive.adoc +++ b/src/asciidoc/web-reactive.adoc @@ -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` 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` -- the controller can use the `Mono` -to declare logic to be executed after the account is deserialized. -* `Single account` -- same as with `Mono` but using RxJava -* `Flux accounts` -- input streaming scenario. -* `Observable 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 { + + private final PersonRepository repository; + + public PersonController(PersonRepository repository) { + this.repository = repository; + } + + @PostMapping("/person") + Mono create(@RequestBody Publisher personStream) { + return this.repository.save(personStream).then(); + } + + @GetMapping("/person") + Flux list() { + return this.repository.findAll(); + } + + @GetMapping("/person/{id}") + Mono findById(@PathVariable String id) { + return this.repository.findOne(id); + } +} +---- -* `Mono` -- serialize without blocking the given Account when the `Mono` completes. -* `Single` -- same but using RxJava. -* `Flux` -- streaming scenario, possibly SSE depending on the requested content type. -* `Observable` -- same but using RxJava `Observable` type. -* `Flowable` -- same but using RxJava 2 `Flowable` type. -* `Flux` -- SSE streaming. -* `Mono` -- 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. +[[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 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 request = ClientRequest.GET("http://example.com/accounts/{id Mono 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` -- the controller can use the `Mono` +to declare logic to be executed after the account is deserialized. +* `Single account` -- same as with `Mono` but using RxJava +* `Flux accounts` -- input streaming scenario. +* `Observable accounts` -- input streaming with RxJava. + +Similarly, the response body can be in any one of the following ways: + +* `Mono` -- serialize without blocking the given Account when the `Mono` completes. +* `Single` -- same but using RxJava. +* `Flux` -- streaming scenario, possibly SSE depending on the requested content type. +* `Observable` -- same but using RxJava `Observable` type. +* `Flowable` -- same but using RxJava 2 `Flowable` type. +* `Flux` -- SSE streaming. +* `Mono` -- 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 = 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`]