Doc use of reactive types in Spring MVC controllers

Issue: SPR-15365
This commit is contained in:
Rossen Stoyanchev
2017-04-04 17:32:24 -04:00
parent f716c8e9bc
commit f293c4d84b
4 changed files with 111 additions and 59 deletions

View File

@@ -34,7 +34,7 @@ import org.springframework.core.annotation.AliasFor;
* types, in arbitrary order (except for validation results, which need to * types, in arbitrary order (except for validation results, which need to
* follow right after the corresponding command object, if desired): * follow right after the corresponding command object, if desired):
* <ul> * <ul>
* <li>Request and/or response objects (typically from the Servlet API). * <li>Request and/or response objects from the Servlet API.
* You may choose any specific request/response type, e.g. * You may choose any specific request/response type, e.g.
* {@link javax.servlet.ServletRequest} / {@link javax.servlet.http.HttpServletRequest}. * {@link javax.servlet.ServletRequest} / {@link javax.servlet.http.HttpServletRequest}.
* <li>Session object: typically {@link javax.servlet.http.HttpSession}. * <li>Session object: typically {@link javax.servlet.http.HttpSession}.
@@ -60,7 +60,7 @@ import org.springframework.core.annotation.AliasFor;
* the response's content. This will be the raw OutputStream/Writer as * the response's content. This will be the raw OutputStream/Writer as
* exposed by the Servlet API. * exposed by the Servlet API.
* <li>{@link org.springframework.http.HttpMethod} for the HTTP request method</li> * <li>{@link org.springframework.http.HttpMethod} for the HTTP request method</li>
* <li>{@link PathVariable @PathVariable} annotated parameters (Servlet-only) * <li>{@link PathVariable @PathVariable} annotated parameters
* for access to URI template values (i.e. /hotels/{hotel}). Variable values will be * for access to URI template values (i.e. /hotels/{hotel}). Variable values will be
* converted to the declared method argument type. By default, the URI template * converted to the declared method argument type. By default, the URI template
* will match against the regular expression {@code [^\.]*} (i.e. any character * will match against the regular expression {@code [^\.]*} (i.e. any character
@@ -69,7 +69,7 @@ import org.springframework.core.annotation.AliasFor;
* Additionally, {@code @PathVariable} can be used on a * Additionally, {@code @PathVariable} can be used on a
* {@link java.util.Map Map&lt;String, String&gt;} to gain access to all * {@link java.util.Map Map&lt;String, String&gt;} to gain access to all
* URI template variables. * URI template variables.
* <li>{@link MatrixVariable @MatrixVariable} annotated parameters (Servlet-only) * <li>{@link MatrixVariable @MatrixVariable} annotated parameters
* for access to name-value pairs located in URI path segments. Matrix variables * for access to name-value pairs located in URI path segments. Matrix variables
* must be represented with a URI template variable. For example /hotels/{hotel} * must be represented with a URI template variable. For example /hotels/{hotel}
* where the incoming URL may be "/hotels/42;q=1". * where the incoming URL may be "/hotels/42;q=1".
@@ -89,7 +89,7 @@ import org.springframework.core.annotation.AliasFor;
* {@link org.springframework.util.MultiValueMap MultiValueMap&lt;String, String&gt;}, or * {@link org.springframework.util.MultiValueMap MultiValueMap&lt;String, String&gt;}, or
* {@link org.springframework.http.HttpHeaders HttpHeaders} method parameter to * {@link org.springframework.http.HttpHeaders HttpHeaders} method parameter to
* gain access to all request headers. * gain access to all request headers.
* <li>{@link RequestBody @RequestBody} annotated parameters (Servlet-only) * <li>{@link RequestBody @RequestBody} annotated parameters
* for access to the Servlet request HTTP contents. The request stream will be * for access to the Servlet request HTTP contents. The request stream will be
* converted to the declared method argument type using * converted to the declared method argument type using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message * {@linkplain org.springframework.http.converter.HttpMessageConverter message
@@ -98,9 +98,7 @@ import org.springframework.core.annotation.AliasFor;
* {@link org.springframework.validation.Errors} argument. * {@link org.springframework.validation.Errors} argument.
* Instead a {@link org.springframework.web.bind.MethodArgumentNotValidException} * Instead a {@link org.springframework.web.bind.MethodArgumentNotValidException}
* exception is raised. * exception is raised.
* <li>{@link RequestPart @RequestPart} annotated parameters * <li>{@link RequestPart @RequestPart} annotated parameters for access to the content
* (Servlet-only, {@literal @MVC 3.1-only})
* for access to the content
* of a part of "multipart/form-data" request. The request part stream will be * of a part of "multipart/form-data" request. The request part stream will be
* converted to the declared method argument type using * converted to the declared method argument type using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message * {@linkplain org.springframework.http.converter.HttpMessageConverter message
@@ -116,15 +114,15 @@ import org.springframework.core.annotation.AliasFor;
* <li>{@link RequestAttribute @RequestAttribute} annotated parameters for access * <li>{@link RequestAttribute @RequestAttribute} annotated parameters for access
* to request attributes. * to request attributes.
* <li>{@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} parameters * <li>{@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} parameters
* (Servlet-only) for access to the Servlet request HTTP headers and contents. * for access to the Servlet request HTTP headers and contents.
* The request stream will be converted to the entity body using * The request stream will be converted to the entity body using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message * {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}. * converters}.
* <li>{@link java.util.Map} / {@link org.springframework.ui.Model} / * <li>{@link java.util.Map} / {@link org.springframework.ui.Model} /
* {@link org.springframework.ui.ModelMap} for enriching the implicit model * {@link org.springframework.ui.ModelMap} for enriching the implicit model
* that will be exposed to the web view. * that will be exposed to the web view.
* <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes} * <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes},
* (Servlet-only, {@literal @MVC 3.1-only}) to specify the exact set of attributes * to specify the exact set of attributes
* to use in case of a redirect and also to add flash attributes (attributes * to use in case of a redirect and also to add flash attributes (attributes
* stored temporarily on the server-side to make them available to the request * stored temporarily on the server-side to make them available to the request
* after the redirect). {@code RedirectAttributes} is used instead of the * after the redirect). {@code RedirectAttributes} is used instead of the
@@ -147,7 +145,6 @@ import org.springframework.core.annotation.AliasFor;
* attributes that have been indicated by the {@link SessionAttributes @SessionAttributes} * attributes that have been indicated by the {@link SessionAttributes @SessionAttributes}
* annotation at the handler type level). * annotation at the handler type level).
* <li>{@link org.springframework.web.util.UriComponentsBuilder} * <li>{@link org.springframework.web.util.UriComponentsBuilder}
* (Servlet-only, {@literal @MVC 3.1-only})
* for preparing a URL relative to the current request's host, port, scheme, * for preparing a URL relative to the current request's host, port, scheme,
* context path, and the literal part of the servlet mapping. * context path, and the literal part of the servlet mapping.
* </ul> * </ul>
@@ -160,64 +157,63 @@ import org.springframework.core.annotation.AliasFor;
* *
* <p>The following return types are supported for handler methods: * <p>The following return types are supported for handler methods:
* <ul> * <ul>
* <li>A {@code ModelAndView} object (from Servlet MVC), * <li>{@code ModelAndView} object (from Servlet MVC),
* with the model implicitly enriched with command objects and the results * with the model implicitly enriched with command objects and the results
* of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods.
* <li>A {@link org.springframework.ui.Model Model} object, with the view name implicitly * <li>{@link org.springframework.ui.Model Model} object, with the view name implicitly
* determined through a {@link org.springframework.web.servlet.RequestToViewNameTranslator} * determined through a {@link org.springframework.web.servlet.RequestToViewNameTranslator}
* and the model implicitly enriched with command objects and the results * and the model implicitly enriched with command objects and the results
* of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods.
* <li>A {@link java.util.Map} object for exposing a model, * <li>{@link java.util.Map} object for exposing a model,
* with the view name implicitly determined through a * with the view name implicitly determined through a
* {@link org.springframework.web.servlet.RequestToViewNameTranslator} * {@link org.springframework.web.servlet.RequestToViewNameTranslator}
* and the model implicitly enriched with command objects and the results * and the model implicitly enriched with command objects and the results
* of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods.
* <li>A {@link org.springframework.web.servlet.View} object, with the * <li>{@link org.springframework.web.servlet.View} object, with the
* model implicitly determined through command objects and * model implicitly determined through command objects and
* {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods.
* The handler method may also programmatically enrich the model by * The handler method may also programmatically enrich the model by
* declaring a {@link org.springframework.ui.Model} argument (see above). * declaring a {@link org.springframework.ui.Model} argument (see above).
* <li>A {@link String} value which is interpreted as view name, * <li>{@link String} value which is interpreted as view name,
* with the model implicitly determined through command objects and * with the model implicitly determined through command objects and
* {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods.
* The handler method may also programmatically enrich the model by * The handler method may also programmatically enrich the model by
* declaring a {@link org.springframework.ui.ModelMap} argument * declaring a {@link org.springframework.ui.ModelMap} argument
* (see above). * (see above).
* <li>{@link ResponseBody @ResponseBody} annotated methods (Servlet-only) * <li>{@link ResponseBody @ResponseBody} annotated methods
* for access to the Servlet response HTTP contents. The return value will * for access to the Servlet response HTTP contents. The return value will
* be converted to the response stream using * be converted to the response stream using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message * {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}. * converters}.
* <li>An {@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} or * <li>{@link org.springframework.http.HttpEntity HttpEntity&lt;?&gt;} or
* {@link org.springframework.http.ResponseEntity ResponseEntity&lt;?&gt;} object * {@link org.springframework.http.ResponseEntity ResponseEntity&lt;?&gt;} object
* (Servlet-only) to access to the Servlet response HTTP headers and contents. * to access to the Servlet response HTTP headers and contents.
* The entity body will be converted to the response stream using * The entity body will be converted to the response stream using
* {@linkplain org.springframework.http.converter.HttpMessageConverter message * {@linkplain org.springframework.http.converter.HttpMessageConverter message
* converters}. * converters}.
* <li>An {@link org.springframework.http.HttpHeaders HttpHeaders} object to * <li>{@link org.springframework.http.HttpHeaders HttpHeaders} object to
* return a response with no body.</li> * return a response with no body.</li>
* <li>A {@link Callable} which is used by Spring MVC to obtain the return * <li>{@link Callable} -- async computation in a Spring MVC managed thread.</li>
* value asynchronously in a separate thread transparently managed by Spring MVC * <li>{@link org.springframework.web.context.request.async.DeferredResult DeferredResult} --
* on behalf of the application. * async result produced later from an application managed, or any thread.
* <li>A {@link org.springframework.web.context.request.async.DeferredResult} * <li>{@link org.springframework.util.concurrent.ListenableFuture ListenableFuture}
* which the application uses to produce a return value in a separate * alternative, equivalent to {@code DeferredResult}.</li>
* thread of its own choosing, as an alternative to returning a Callable. * <li>{@link java.util.concurrent.CompletionStage CompletionStage} and
* <li>A {@link org.springframework.util.concurrent.ListenableFuture} * {@link java.util.concurrent.CompletableFuture CompletableFuture} --
* which the application uses to produce a return value in a separate * alternative, equivalent to {@code DeferredResult}.</li>
* thread of its own choosing, as an alternative to returning a Callable. * <li>{@link org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter} --
* <li>A {@link java.util.concurrent.CompletionStage} (implemented by * {@code @ResponseBody}-style, asynchronous writing of a stream of Objects to the
* {@link java.util.concurrent.CompletableFuture} for example) * response body.</li>
* which the application uses to produce a return value in a separate * <li>{@link org.springframework.web.servlet.mvc.method.annotation.SseEmitter} --
* thread of its own choosing, as an alternative to returning a Callable. * variant of {@code ResponseBodyEmitter} for a Server-Sent Events formatted stream.</li>
* <li>A {@link org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter} * <li>{@link org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody} --
* can be used to write multiple objects to the response asynchronously; * async writing directly to the response body {@link java.io.OutputStream}.</li>
* also supported as the body within {@code ResponseEntity}.</li> * <li>Reactive types (e.g. Reactor {@code Flux}/{@code Mono}, RxJava 1 &amp; 2
* <li>An {@link org.springframework.web.servlet.mvc.method.annotation.SseEmitter} * {@code Observable}/{@code Single}, or others via
* can be used to write Server-Sent Events to the response asynchronously; * {@link org.springframework.core.ReactiveAdapterRegistry ReactiveAdapterRegistry}) --
* also supported as the body within {@code ResponseEntity}.</li> * alternatives, equivalent to {@code DeferredResult} or {@code ResponseBodyEmitter}
* <li>A {@link org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody} * and {@code SseEmitter} depending also on the requested media types (e.g.
* can be used to write to the response asynchronously; * "text/event-stream", "application/json+stream").</li>
* also supported as the body within {@code ResponseEntity}.</li>
* <li>{@code void} if the method handles the response itself (by * <li>{@code void} if the method handles the response itself (by
* writing the response content directly, declaring an argument of type * writing the response content directly, declaring an argument of type
* {@link javax.servlet.ServletResponse} / {@link javax.servlet.http.HttpServletResponse} * {@link javax.servlet.ServletResponse} / {@link javax.servlet.http.HttpServletResponse}

View File

@@ -56,6 +56,11 @@ public class AsyncSupportConfigurer {
* highly recommended to change that default in production since the simple * highly recommended to change that default in production since the simple
* executor does not re-use threads. * executor does not re-use threads.
* *
* <p>As of 5.0 this executor is also used when a controller returns a reactive
* type that does streaming (e.g. "text/event-stream" or
* "application/stream+json") for the blocking writes to the
* {@link javax.servlet.ServletOutputStream}.
*
* @param taskExecutor the task executor instance to use by default * @param taskExecutor the task executor instance to use by default
*/ */
public AsyncSupportConfigurer setTaskExecutor(AsyncTaskExecutor taskExecutor) { public AsyncSupportConfigurer setTaskExecutor(AsyncTaskExecutor taskExecutor) {

View File

@@ -231,9 +231,14 @@
<xsd:attribute name="task-executor" type="xsd:string"> <xsd:attribute name="task-executor" type="xsd:string">
<xsd:annotation> <xsd:annotation>
<xsd:documentation source="java:org.springframework.core.task.AsyncTaskExecutor"><![CDATA[ <xsd:documentation source="java:org.springframework.core.task.AsyncTaskExecutor"><![CDATA[
The bean name of a default AsyncTaskExecutor to use when a controller method returns a {@link Callable}. The bean name of a default AsyncTaskExecutor to use when a controller method returns a "Callable".
Controller methods can override this default on a per-request basis by returning an AsyncTask. Controller methods can override this default on a per-request basis by returning an AsyncTask.
By default, a SimpleAsyncTaskExecutor is used which does not re-use threads and is not recommended for production. By default, a SimpleAsyncTaskExecutor is used which does not re-use threads and is not recommended for production.
As of 5.0 this executor is also used when a controller returns a reactive type that does streaming
(e.g. "text/event-stream" or "application/stream+json") for the blocking writes to the
"javax.servlet.ServletOutputStream".
]]></xsd:documentation> ]]></xsd:documentation>
<xsd:appinfo> <xsd:appinfo>
<tool:annotation kind="ref"> <tool:annotation kind="ref">

View File

@@ -1408,18 +1408,18 @@ of `java.util.Optional` in those cases is equivalent to having `required=false`.
==== Supported method return types ==== Supported method return types
The following are the supported return types: The following are the supported return types:
* A `ModelAndView` object, with the model implicitly enriched with command objects and * `ModelAndView` object, with the model implicitly enriched with command objects and
the results of `@ModelAttribute` annotated reference data accessor methods. the results of `@ModelAttribute` annotated reference data accessor methods.
* A `Model` object, with the view name implicitly determined through a * `Model` object, with the view name implicitly determined through a
`RequestToViewNameTranslator` and the model implicitly enriched with command objects `RequestToViewNameTranslator` and the model implicitly enriched with command objects
and the results of `@ModelAttribute` annotated reference data accessor methods. and the results of `@ModelAttribute` annotated reference data accessor methods.
* A `Map` object for exposing a model, with the view name implicitly determined through * `Map` object for exposing a model, with the view name implicitly determined through
a `RequestToViewNameTranslator` and the model implicitly enriched with command objects a `RequestToViewNameTranslator` and the model implicitly enriched with command objects
and the results of `@ModelAttribute` annotated reference data accessor methods. and the results of `@ModelAttribute` annotated reference data accessor methods.
* A `View` object, with the model implicitly determined through command objects and * `View` object, with the model implicitly determined through command objects and
`@ModelAttribute` annotated reference data accessor methods. The handler method may `@ModelAttribute` annotated reference data accessor methods. The handler method may
also programmatically enrich the model by declaring a `Model` argument (see above). also programmatically enrich the model by declaring a `Model` argument (see above).
* A `String` value that is interpreted as the logical view name, with the model * `String` value that is interpreted as the logical view name, with the model
implicitly determined through command objects and `@ModelAttribute` annotated implicitly determined through command objects and `@ModelAttribute` annotated
reference data accessor methods. The handler method may also programmatically enrich reference data accessor methods. The handler method may also programmatically enrich
the model by declaring a `Model` argument (see above). the model by declaring a `Model` argument (see above).
@@ -1431,22 +1431,25 @@ The following are the supported return types:
* If the method is annotated with `@ResponseBody`, the return type is written to the * If the method is annotated with `@ResponseBody`, the return type is written to the
response HTTP body. The return value will be converted to the declared method argument response HTTP body. The return value will be converted to the declared method argument
type using ``HttpMessageConverter``s. See <<mvc-ann-responsebody>>. type using ``HttpMessageConverter``s. See <<mvc-ann-responsebody>>.
* An `HttpEntity<?>` or `ResponseEntity<?>` object to provide access to the Servlet * `HttpEntity<?>` or `ResponseEntity<?>` object to provide access to the Servlet
response HTTP headers and contents. The entity body will be converted to the response response HTTP headers and contents. The entity body will be converted to the response
stream using ``HttpMessageConverter``s. See <<mvc-ann-httpentity>>. stream using ``HttpMessageConverter``s. See <<mvc-ann-httpentity>>.
* An `HttpHeaders` object to return a response with no body. * `HttpHeaders` object to return a response with no body.
* A `Callable<?>` can be returned when the application wants to produce the return value * `Callable<?>` async computation in a Spring MVC managed thread.
asynchronously in a thread managed by Spring MVC. * `DeferredResult<?>` async result produced later from an application managed, or any thread.
* A `DeferredResult<?>` can be returned when the application wants to produce the return * `ListenableFuture<?>` as an alternative equivalent to using `DeferredResult`.
value from a thread of its own choosing. * `CompletableFuture<?>` or `CompletionStage<?>` as an alternative equivalent to `DeferredResult`.
* A `ListenableFuture<?>` or `CompletableFuture<?>`/`CompletionStage<?>` can be returned * `ResponseBodyEmitter` can be returned to write multiple objects to the response
when the application wants to produce the value from a thread pool submission.
* A `ResponseBodyEmitter` can be returned to write multiple objects to the response
asynchronously; also supported as the body within a `ResponseEntity`. asynchronously; also supported as the body within a `ResponseEntity`.
* An `SseEmitter` can be returned to write Server-Sent Events to the response * `SseEmitter` can be returned to write Server-Sent Events to the response
asynchronously; also supported as the body within a `ResponseEntity`. asynchronously; also supported as the body within a `ResponseEntity`.
* A `StreamingResponseBody` can be returned to write to the response OutputStream * `StreamingResponseBody` can be returned to write to the response OutputStream
asynchronously; also supported as the body within a `ResponseEntity`. asynchronously; also supported as the body within a `ResponseEntity`.
* Reactive types from Reactor 3, RxJava 2, RxJava 1 or others registered through
the configured `ReactiveAdapterRegistry` can be returned as an alternative
equivalent to using `DeferredResult` for single-valued types, or
`ResponseBodyEmitter` and `SseEmitter` for multi-valued reactive types where a streaming
media type (e.g. "text/event-stream", "application/json+stream") is requested.
* Any other return type is considered to be a single model attribute to be exposed to * Any other return type is considered to be a single model attribute to be exposed to
the view, using the attribute name specified through `@ModelAttribute` at the method the view, using the attribute name specified through `@ModelAttribute` at the method
level (or the default attribute name based on the return type class name). The model level (or the default attribute name based on the return type class name). The model
@@ -2517,6 +2520,49 @@ Note that `StreamingResponseBody` can also be used as the body in a
the response. the response.
[[mvc-ann-async-reactive-types]]
==== Async Requests with Reactive Types
If using the reactive `WebClient` from `spring-webflux`, or another client, or
a data store with reactive support, you can return reactive types directly from
Spring MVC controller methods.
* If the return type has single-value stream semantics such as Reactor `Mono` or
RxJava `Single` it is adapted and equivalent to using `DeferredResult`.
* If the return type has multi-value stream semantics such as Reactor `Flux` or
RxJava `Observable` / `Flowable` and if the media type indicates streaming, e.g.
"application/stream+json" or "text/event-stream", it is adapted and equivalent to
using `ResponseBodyEmitter` or `SseEmitter`. You can also return
`Flux<ServerSentEvent>` or `Observable<ServerSentEvent>`.
* If the return type has multi-value stream semantics but the media type does not
imply streaming, e.g. "application/json", it is adapted and equivalent to using
`DeferredResult<List<?>>`, e.g. JSON array.
Reactive libraries are detected and adapted to a Reactive Streams `Publisher`
through Spring's pluggable `ReactiveAdapterRegistry` which by default supports
Reactor 3, RxJava 2, and RxJava 1. Note that for RxJava 1 you will need to add
https://github.com/ReactiveX/RxJavaReactiveStreams["io.reactivex:rxjava-reactive-streams"]
to the classpath.
A common assumption with reactive libraries is not block the processing thread.
The `WebClient` with Reactor Netty for example is based on event-loop style
handling using a small, fixed number of threads and those must not be blocked
when writing to the `ServletResponseOutputStream`. Reactive libraries have
operators for that but Spring MVC automatically writes asynchronously so you
don't need to use that. The underlying `TaskExecutor` for this can be configured
through the MVC Java config and the MVC namespace as described in the following
section.
[NOTE]
====
Unlike Spring MVC, Spring WebFlux is built on a non-blocking, reactive foundation
and uses the Servlet 3.1 non-blocking I/O that's also based on event loop style
processing and hence does not require a thread to absorb the effect of blocking.
====
[[mvc-ann-async-configuration]] [[mvc-ann-async-configuration]]
==== Configuring Asynchronous Request Processing ==== Configuring Asynchronous Request Processing