From 391752abc2880de14471841f5a0a627a4cf3c802 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 30 Aug 2016 14:36:19 -0400 Subject: [PATCH] Polish and update reactive getting started reference This commit updates the instructions on getting started with Spring Web Reactive and also updates constructors and setters to streamline the getting started procedure. Issue: SPR-14640 --- .../web/reactive/DispatcherHandler.java | 8 ++ .../reactive/DispatcherHandlerErrorTests.java | 3 +- ...mpleUrlHandlerMappingIntegrationTests.java | 5 +- ...bstractRequestMappingIntegrationTests.java | 6 +- .../annotation/SseIntegrationTests.java | 5 +- .../reactive/UndertowHttpHandlerAdapter.java | 12 ++- .../bootstrap/UndertowHttpServer.java | 10 +-- src/asciidoc/web-reactive.adoc | 74 ++++++++++++------- 8 files changed, 70 insertions(+), 53 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java index 7344d34db4..bbd560d1de 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/DispatcherHandler.java @@ -71,6 +71,14 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware { private List resultHandlers; + public DispatcherHandler() { + } + + public DispatcherHandler(ApplicationContext applicationContext) { + initStrategies(applicationContext); + } + + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { initStrategies(applicationContext); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java index 7b1cb9e2e6..e2341ac919 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java @@ -87,8 +87,7 @@ public class DispatcherHandlerErrorTests { appContext.register(TestConfig.class); appContext.refresh(); - this.dispatcherHandler = new DispatcherHandler(); - this.dispatcherHandler.setApplicationContext(appContext); + this.dispatcherHandler = new DispatcherHandler(appContext); this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("/")); MockServerHttpResponse response = new MockServerHttpResponse(); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java index 46668a574a..c45cd04dce 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java @@ -61,10 +61,7 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler wac.register(WebConfig.class); wac.refresh(); - DispatcherHandler dispatcherHandler = new DispatcherHandler(); - dispatcherHandler.setApplicationContext(wac); - - return WebHttpHandlerBuilder.webHandler(dispatcherHandler) + return WebHttpHandlerBuilder.webHandler(new DispatcherHandler(wac)) .exceptionHandlers(new ResponseStatusExceptionHandler()) .build(); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/AbstractRequestMappingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/AbstractRequestMappingIntegrationTests.java index 2beb97217f..b3847bc365 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/AbstractRequestMappingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/AbstractRequestMappingIntegrationTests.java @@ -44,9 +44,9 @@ public abstract class AbstractRequestMappingIntegrationTests extends AbstractHtt @Override protected HttpHandler createHttpHandler() { this.applicationContext = initApplicationContext(); - DispatcherHandler handler = new DispatcherHandler(); - handler.setApplicationContext(this.applicationContext); - return WebHttpHandlerBuilder.webHandler(handler).build(); + return WebHttpHandlerBuilder + .webHandler(new DispatcherHandler(this.applicationContext)) + .build(); } protected abstract ApplicationContext initApplicationContext(); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java index dbf259b82f..559eafc9d3 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java @@ -65,10 +65,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests { this.wac.register(TestConfiguration.class); this.wac.refresh(); - DispatcherHandler webHandler = new DispatcherHandler(); - webHandler.setApplicationContext(this.wac); - - return WebHttpHandlerBuilder.webHandler(webHandler).build(); + return WebHttpHandlerBuilder.webHandler(new DispatcherHandler(this.wac)).build(); } @Test diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java index f8b65e5b96..7126037582 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowHttpHandlerAdapter.java @@ -23,6 +23,7 @@ import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.util.Assert; /** @@ -40,17 +41,20 @@ public class UndertowHttpHandlerAdapter implements io.undertow.server.HttpHandle private final HttpHandler delegate; - private final DataBufferFactory dataBufferFactory; + private DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(false); - public UndertowHttpHandlerAdapter(HttpHandler delegate, DataBufferFactory dataBufferFactory) { + public UndertowHttpHandlerAdapter(HttpHandler delegate) { Assert.notNull(delegate, "'delegate' is required"); - Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null"); this.delegate = delegate; - this.dataBufferFactory = dataBufferFactory; } + public void setDataBufferFactory(DataBufferFactory dataBufferFactory) { + Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null"); + this.dataBufferFactory = dataBufferFactory; + } + @Override public void handleRequest(HttpServerExchange exchange) throws Exception { diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/bootstrap/UndertowHttpServer.java b/spring-web/src/test/java/org/springframework/http/server/reactive/bootstrap/UndertowHttpServer.java index ca9555a816..ee3ab6dafa 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/bootstrap/UndertowHttpServer.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/bootstrap/UndertowHttpServer.java @@ -19,8 +19,6 @@ package org.springframework.http.server.reactive.bootstrap; import io.undertow.Undertow; import io.undertow.server.HttpHandler; -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter; import org.springframework.util.Assert; @@ -31,19 +29,13 @@ public class UndertowHttpServer extends HttpServerSupport implements HttpServer private Undertow server; - private DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); - private boolean running; - public void setDataBufferFactory(DataBufferFactory dataBufferFactory) { - this.dataBufferFactory = dataBufferFactory; - } @Override public void afterPropertiesSet() throws Exception { Assert.notNull(getHttpHandler()); - HttpHandler handler = - new UndertowHttpHandlerAdapter(getHttpHandler(), dataBufferFactory); + HttpHandler handler = new UndertowHttpHandlerAdapter(getHttpHandler()); this.server = Undertow.builder().addHttpListener(getPort(), getHost()) .setHandler(handler).build(); } diff --git a/src/asciidoc/web-reactive.adoc b/src/asciidoc/web-reactive.adoc index cc0ec7c605..324f4ebdfe 100644 --- a/src/asciidoc/web-reactive.adoc +++ b/src/asciidoc/web-reactive.adoc @@ -153,8 +153,10 @@ Single response = webClient [[web-reactive-getting-started-boot]] === Spring Boot Starter -The experimental Spring Boot Web Reactive starter available via http://start.spring.io -is the quickest way to get started. It does all the work so you can start +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 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. @@ -162,39 +164,57 @@ be changed as usual with Spring Boot to switch to a different runtime. [[web-reactive-getting-started-manual]] === Manual Bootstrapping -It is also easy to get started by writing a few lines of code: +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` +(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` +* RxNetty -- `io.reactivex:rxnetty-common` and `io.reactivex:rxnetty-http` +* Undertow -- `io.undertow:undertow-core` + +For the bootstrap code start with: +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); +context.register(WebReactiveConfiguration.class); // (1) +context.refresh(); + +DispatcherHandler dispatcherHandler = new DispatcherHandler(context); // (2) +HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(dispatcherHandler).build(); +---- + +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: [source,java,indent=0] [subs="verbatim,quotes"] ---- -AnnotationConfigApplicationContext context; -context = new AnnotationConfigApplicationContext(); -context.register(WebReactiveConfiguration.class); // (1) -context.refresh(); +// Tomcat and Jetty +HttpServlet servlet = new ServletHttpHandlerAdapter(httpHandler); +... -DispatcherHandler handler = new DispatcherHandler(); // (2) -handler.setApplicationContext(context); -HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(handler).build(); +// Reactor Netty +HttpServer server = HttpServer.create(host, port) +server.startAndAwait(new ServletHttpHandlerAdapter(httpHandler)); -HttpServer server = new TomcatHttpServer(); // (3) -server.setPort(8080); -server.setHandler(httpHandler); -server.afterPropertiesSet(); -server.start(); +// RxNetty +HttpServer server = HttpServer.newServer(new InetSocketAddress(host, port)) +server.startAndAwait(new RxNettyHttpHandlerAdapter(httpHandler)); + +// Undertow +Undertow.builder().addHttpListener(port, host) + .setHandler(new UndertowHttpHandlerAdapter(httpHandler)) + .build() ---- -The `WebReactiveConfiguration` at (1) is the Java config from `spring-web-reactive` -and is similar in purpose to the MVC Java config from `spring-webmvc`. It provides the -web framework configuration required to get started leaving you only to -declare your own `@Controller` beans. - -The `DispatcherHandler` at (2) is the equivalent of the `DispatcherServlet` in Spring MVC. - -The `HttpServer` at (3) is an abstraction from the -https://github.com/spring-projects/spring-framework/tree/master/spring-web/src/test/java/org/springframework/http/server/reactive/bootstrap[test sources] -of `spring-web-reactive` used for Spring Framework's own integration tests. -The abstraction comes with basic implementations for each supported runtime. - [[web-reactive-getting-started-M1]] === Extent of Support in 5.0 M1