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