Skip to content

Commit 209e8de

Browse files
committed
Document async request processing enhancements
Generally update chapter and add documentation for 4.2 including the return value types ResponseBodyEmitter, SseEmitter, and StreamingResponseBody. Issue: SPR-12672
1 parent 5b09723 commit 209e8de

File tree

1 file changed

+184
-104
lines changed

1 file changed

+184
-104
lines changed

src/asciidoc/web-mvc.adoc

Lines changed: 184 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,12 +2054,12 @@ customized through `jsonpParameterNames` property.
20542054
=== Asynchronous Request Processing
20552055
Spring MVC 3.2 introduced Servlet 3 based asynchronous request processing. Instead of
20562056
returning a value, as usual, a controller method can now return a
2057-
`java.util.concurrent.Callable` and produce the return value from a separate thread.
2058-
Meanwhile the main Servlet container thread is released and allowed to process other
2057+
`java.util.concurrent.Callable` and produce the return value from a Spring MVC managed thread.
2058+
Meanwhile the main Servlet container thread is exited and released and allowed to process other
20592059
requests. Spring MVC invokes the `Callable` in a separate thread with the help of a
20602060
`TaskExecutor` and when the `Callable` returns, the request is dispatched back to the
2061-
Servlet container to resume processing with the value returned by the `Callable`. Here
2062-
is an example controller method:
2061+
Servlet container to resume processing using the value returned by the `Callable`. Here
2062+
is an example of such a controller method:
20632063

20642064
[source,java,indent=0]
20652065
[subs="verbatim,quotes"]
@@ -2077,11 +2077,11 @@ is an example controller method:
20772077
}
20782078
----
20792079

2080-
A second option is for the controller to return an instance of `DeferredResult`. In this
2081-
case the return value will also be produced from a separate thread. However, that thread
2082-
is not known to Spring MVC. For example the result may be produced in response to some
2083-
external event such as a JMS message, a scheduled task, etc. Here is an example
2084-
controller method:
2080+
Another option is for the controller method to return an instance of `DeferredResult`. In this
2081+
case the return value will also be produced from any thread, i.e. one that
2082+
is not managed by Spring MVC. For example the result may be produced in response to some
2083+
external event such as a JMS message, a scheduled task, and so on. Here is an example
2084+
of such a controller method:
20852085

20862086
[source,java,indent=0]
20872087
[subs="verbatim,quotes"]
@@ -2090,110 +2090,195 @@ controller method:
20902090
@ResponseBody
20912091
public DeferredResult<String> quotes() {
20922092
DeferredResult<String> deferredResult = new DeferredResult<String>();
2093-
// Save the deferredResult in in-memory queue ...
2093+
// Save the deferredResult somewhere..
20942094
return deferredResult;
20952095
}
20962096
20972097
// In some other thread...
20982098
deferredResult.setResult(data);
20992099
----
21002100

2101-
This may be difficult to understand without any knowledge of the Servlet 3 async
2102-
processing feature. It would certainly help to read up on it. At a very minimum consider
2103-
the following basic facts:
2101+
This may be difficult to understand without any knowledge of the Servlet 3.0
2102+
asynchronous request processing features. It would certainly help to read up
2103+
on that. Here are a few basic facts about the underlying mechanism:
21042104

21052105
* A `ServletRequest` can be put in asynchronous mode by calling `request.startAsync()`.
21062106
The main effect of doing so is that the Servlet, as well as any Filters, can exit but
2107-
the response will remain open allowing some other thread to complete processing.
2108-
* The call to `request.startAsync()` returns an `AsyncContext`, which can be used for
2107+
the response will remain open to allow processing to complete later.
2108+
* The call to `request.startAsync()` returns `AsyncContext` which can be used for
21092109
further control over async processing. For example it provides the method `dispatch`,
2110-
which can be called from an application thread in order to "dispatch" the request back
2111-
to the Servlet container. An async dispatch is similar to a forward except it is made
2112-
from one (application) thread to another (Servlet container) thread whereas a forward
2113-
occurs synchronously in the same (Servlet container) thread.
2114-
* `ServletRequest` provides access to the current `DispatcherType`, which can be used to
2115-
distinguish if a `Servlet` or a `Filter` is processing on the initial request
2116-
processing thread and when it is processing in an async dispatch.
2110+
that is similar to a forward from the Servlet API except it allows an
2111+
application to resume request processing on a Servlet container thread.
2112+
* The `ServletRequest` provides access to the current `DispatcherType` that can
2113+
be used to distinguish between processing the initial request, an async
2114+
dispatch, a forward, and other dispatcher types.
21172115

21182116
With the above in mind, the following is the sequence of events for async request
2119-
processing with a `Callable`: (1) Controller returns a `Callable`, (2) Spring MVC starts
2120-
async processing and submits the `Callable` to a `TaskExecutor` for processing in a
2121-
separate thread, (3) the `DispatcherServlet` and all Filter's exit the request
2122-
processing thread but the response remains open, (4) the `Callable` produces a result
2123-
and Spring MVC dispatches the request back to the Servlet container, (5) the
2124-
`DispatcherServlet` is invoked again and processing resumes with the asynchronously
2125-
produced result from the `Callable`. The exact sequencing of (2), (3), and (4) may vary
2126-
depending on the speed of execution of the concurrent threads.
2127-
2128-
The sequence of events for async request processing with a `DeferredResult` is the same
2129-
in principal except it's up to the application to produce the asynchronous result from
2130-
some thread: (1) Controller returns a `DeferredResult` and saves it in some in-memory
2131-
queue or list where it can be accessed, (2) Spring MVC starts async processing, (3) the
2132-
`DispatcherServlet` and all configured Filter's exit the request processing thread but
2133-
the response remains open, (4) the application sets the `DeferredResult` from some
2134-
thread and Spring MVC dispatches the request back to the Servlet container, (5) the
2135-
`DispatcherServlet` is invoked again and processing resumes with the asynchronously
2136-
produced result.
2137-
2138-
Explaining the motivation for async request processing and when or why to use it are
2139-
beyond the scope of this document. For further information you may wish to read
2117+
processing with a `Callable`:
2118+
2119+
* Controller returns a `Callable`.
2120+
* Spring MVC starts asynchronous processing and submits the `Callable` to
2121+
a `TaskExecutor` for processing in a separate thread.
2122+
* The `DispatcherServlet` and all Filter's exit the Servlet container thread
2123+
but the response remains open.
2124+
* The `Callable` produces a result and Spring MVC dispatches the request back
2125+
to the Servlet container to resume processing.
2126+
* The `DispatcherServlet` is invoked again and processing resumes with the
2127+
asynchronously produced result from the `Callable`.
2128+
2129+
The sequence for `DeferredResult` is very similar except it's up to the
2130+
application to produce the asynchronous result from any thread:
2131+
2132+
* Controller returns a `DeferredResult` and saves it in some in-memory
2133+
queue or list where it can be accessed.
2134+
* Spring MVC starts async processing.
2135+
* The `DispatcherServlet` and all configured Filter's exit the request
2136+
processing thread but the response remains open.
2137+
* The application sets the `DeferredResult` from some thread and Spring MVC
2138+
dispatches the request back to the Servlet container.
2139+
* The `DispatcherServlet` is invoked again and processing resumes with the
2140+
asynchronously produced result.
2141+
2142+
For further background on the motivation for async request processing and
2143+
when or why to use it please read
21402144
https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[this
21412145
blog post series].
21422146

21432147

21442148
[[mvc-ann-async-exceptions]]
21452149
==== Exception Handling for Async Requests
2146-
What happens if a `Callable` returned from a controller method raises an Exception while
2147-
being executed? The effect is similar to what happens when any controller method raises
2148-
an exception. It is handled by a matching `@ExceptionHandler` method in the same
2149-
controller or by one of the configured `HandlerExceptionResolver` instances.
2150-
2151-
[NOTE]
2152-
====
2153-
Under the covers, when a `Callable` raises an Exception, Spring MVC still dispatches to
2154-
the Servlet container to resume processing. The only difference is that the result of
2155-
executing the `Callable` is an `Exception` that must be processed with the configured
2156-
`HandlerExceptionResolver` instances.
2157-
====
2158-
2159-
When using a `DeferredResult`, you have a choice of calling its `setErrorResult(Object)`
2160-
method and provide an `Exception` or any other Object you'd like to use as the result.
2161-
If the result is an `Exception`, it will be processed with a matching
2162-
`@ExceptionHandler` method in the same controller or with any configured
2163-
`HandlerExceptionResolver` instance.
2150+
What happens if a `Callable` returned from a controller method raises an
2151+
Exception while being executed? The short answer is the same as what happens
2152+
when a controller method raises an exception. It goes through the regular
2153+
exception handling mechanism. The longer explanation is that when a `Callable`
2154+
raises an Exception Spring MVC dispatches to the Servlet container with
2155+
the `Exception` as the result and that leads to resume request processing
2156+
with the `Exception` instead of a controller method return value.
2157+
When using a `DeferredResult` you have a choice whether to call
2158+
`setResult` or `setErrorResult` with an `Exception` instance.
21642159

21652160

21662161
[[mvc-ann-async-interception]]
21672162
==== Intercepting Async Requests
2168-
An existing `HandlerInterceptor` can implement `AsyncHandlerInterceptor`, which provides
2169-
one additional method `afterConcurrentHandlingStarted`. It is invoked after async
2170-
processing starts and when the initial request processing thread is being exited. See
2171-
the `AsyncHandlerInterceptor` javadocs for more details on that.
2163+
A `HandlerInterceptor` can also implement `AsyncHandlerInterceptor` in order
2164+
to implement the `afterConcurrentHandlingStarted` callback, which is called
2165+
instead of `postHandle` and `afterCompletion` when asynchronous processing
2166+
starts.
2167+
2168+
A `HandlerInterceptor` can also register a `CallableProcessingInterceptor`
2169+
or a `DeferredResultProcessingInterceptor` in order to integrate more
2170+
deeply with the lifecycle of an asynchronous request and for example
2171+
handle a timeout event. See the Javadoc of `AsyncHandlerInterceptor`
2172+
for more details.
2173+
2174+
The `DeferredResult` type also provides methods such as `onTimeout(Runnable)`
2175+
and `onCompletion(Runnable)`. See the Javadoc of `DeferredResult` for more
2176+
details.
2177+
2178+
When using a `Callable` you can wrap it with an instance of `WebAsyncTask`
2179+
which also provides registration methods for timeout and completion.
2180+
2181+
[[mvc-ann-async-http-streaming]]
2182+
==== HTTP Streaming
2183+
2184+
A controller method can use `DeferredResult` and `Callable` to produce its
2185+
return value asynchronously and that can be used to implement techniques such as
2186+
http://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[long polling]
2187+
where the server can push an event to the client as soon as possible.
21722188

2173-
Further options for async request lifecycle callbacks are provided directly on
2174-
`DeferredResult`, which has the methods `onTimeout(Runnable)` and
2175-
`onCompletion(Runnable)`. Those are called when the async request is about to time out
2176-
or has completed respectively. The timeout event can be handled by setting the
2177-
`DeferredResult` to some value. The completion callback however is final and the result
2178-
can no longer be set.
2189+
What if you wanted to push multiple events on a single HTTP response?
2190+
This is a technique related to "Long Polling" that is known as "HTTP Streaming".
2191+
Spring MVC makes this possible through the `ResponseBodyEmitter` return value
2192+
type which can be used to send multiple Objects, instead of one as is normally
2193+
the case with `@ResponseBody`, where each Object sent is written to the
2194+
response with an `HttpMessageConverter`.
2195+
2196+
Here is an example of that:
2197+
2198+
[source,java,indent=0]
2199+
[subs="verbatim,quotes"]
2200+
----
2201+
@RequestMapping("/events")
2202+
public ResponseBodyEmitter<String> handle() {
2203+
ResponseBodyEmitter<String> emitter = new ResponseBodyEmitter<String>();
2204+
// Save the emitter somewhere..
2205+
return emitter;
2206+
}
2207+
2208+
// In some other thread
2209+
emitter.send("Hello once");
2210+
2211+
// and again later on
2212+
emitter.send("Hello again");
2213+
2214+
// and done at some point
2215+
emitter.complete();
2216+
----
21792217

2180-
Similar callbacks are also available with a `Callable`. However, you will need to wrap
2181-
the `Callable` in an instance of `WebAsyncTask` and then use that to register the
2182-
timeout and completion callbacks. Just like with `DeferredResult`, the timeout event can
2183-
be handled and a value can be returned while the completion event is final.
2218+
Note that `ResponseBodyEmitter` can also be used as the body in a
2219+
`ResponseEntity` in order to customize the status and headers of
2220+
the response.
21842221

2185-
You can also register a `CallableProcessingInterceptor` or a
2186-
`DeferredResultProcessingInterceptor` globally through the MVC Java config or the MVC
2187-
namespace. Those interceptors provide a full set of callbacks and apply every time a
2188-
`Callable` or a `DeferredResult` is used.
2222+
[[mvc-ann-async-sse]]
2223+
==== Server-Sent Events
2224+
2225+
`SseEmitter` is a sub-class of `ResponseBodyEmitter` providing support for
2226+
http://www.w3.org/TR/eventsource/[Server-Sent Events].
2227+
Server-sent events is a just another variation on the same "HTTP Streaming"
2228+
technique except events pushed from the server are formatted according to
2229+
the W3C Servet-Sent Events specification.
2230+
2231+
Server-Sent Events can be used for their intended purpose, that is to push
2232+
events from the server to clients. It is quite easy to do in Spring MVC and
2233+
requires simply returning a value of type `SseEmitter`.
2234+
2235+
Note however that Internet Explorer does not support Server-Sent Events and
2236+
that for more advanced web application messaging scenarios such as online games,
2237+
collaboration, financial applicatinos, and others it's better to consider
2238+
Spring's WebSocket support that includes SockJS-style WebSocket emulation
2239+
falling back to a very wide range of browsers (including Internet Explorer)
2240+
and also higher-level messaging patterns for interacting with clients through
2241+
a publish-subscribe model within a more messaging-centric architecture.
2242+
For further background on this see
2243+
http://blog.pivotal.io/pivotal/products/websocket-architecture-in-spring-4-0[the following blog post].
2244+
2245+
[[mvc-ann-async-output-stream]]
2246+
==== HTTP Streaming Directly To The OutputStream
2247+
2248+
`ResponseBodyEmitter` allows sending events by writing Objects to the
2249+
response through an `HttpMessageConverter`. This is probably the most common
2250+
case, for example when writing JSON data. However sometimes it is useful to
2251+
bypass message conversion and write directly to the response `OutputStream`
2252+
for example for a file download. This can be done with the help of the
2253+
`StreamingResponseBody` return value type.
2254+
2255+
Here is an example of that:
2256+
2257+
[source,java,indent=0]
2258+
[subs="verbatim,quotes"]
2259+
----
2260+
@RequestMapping("/download")
2261+
public StreamingResponseBody handle() {
2262+
return new StreamingResponseBody() {
2263+
@Override
2264+
public void writeTo(OutputStream outputStream) throws IOException {
2265+
// write...
2266+
}
2267+
};
2268+
}
2269+
----
2270+
2271+
Note that `StreamingResponseBody` can also be used as the body in a
2272+
`ResponseEntity` in order to customize the status and headers of
2273+
the response.
21892274

21902275

21912276
[[mvc-ann-async-configuration]]
2192-
==== Configuration for Async Request Processing
2277+
==== Configuring Asynchronous Request Processing
21932278

21942279
[[mvc-ann-async-configuration-servlet3]]
2195-
===== Servlet 3 Async Config
2196-
To use Servlet 3 async request processing, you need to update `web.xml` to version 3.0:
2280+
===== Servlet Container Configuration
2281+
For applications configured with a `web.xml` be sure to update to version 3.0:
21972282

21982283
[source,xml,indent=0]
21992284
[subs="verbatim,quotes"]
@@ -2209,22 +2294,15 @@ To use Servlet 3 async request processing, you need to update `web.xml` to versi
22092294
</web-app>
22102295
----
22112296

2212-
The `DispatcherServlet` and any `Filter` configuration need to have the
2213-
`<async-supported>true</async-supported>` sub-element. Additionally, any `Filter` that
2214-
also needs to get involved in async dispatches should also be configured to support the
2215-
ASYNC dispatcher type. Note that it is safe to enable the ASYNC dispatcher type for all
2216-
filters provided with the Spring Framework since they will not get involved in async
2217-
dispatches unless needed.
2297+
Asynchronous support must be enabled on the `DispatcherServlet` through the
2298+
`<async-supported>true</async-supported>` web.xml sub-element. Additionally
2299+
any `Filter` that participates in asyncrequest processing must be configured
2300+
to support the ASYNC dispatcher type. It should be safe to enable the ASYNC
2301+
dispatcher type for all filters provided with the Spring Framework since they
2302+
usually extend `OncePerRequestFilter` and that has runtime checks for whether
2303+
the filter needs to be involved in async dispatches or not.
22182304

2219-
[WARNING]
2220-
====
2221-
Note that for some Filters it is absolutely critical to ensure they are mapped to
2222-
be invoked during asynchronous dispatches. For example if a filter such as the
2223-
`OpenEntityManagerInViewFilter` is responsible for releasing database connection
2224-
resources and must be invoked at the end of an async request.
2225-
2226-
Below is an example of a propertly configured filter:
2227-
====
2305+
Below is some example web.xml configuration:
22282306

22292307
[source,xml,indent=0]
22302308
[subs="verbatim,quotes"]
@@ -2253,18 +2331,20 @@ Below is an example of a propertly configured filter:
22532331
22542332
----
22552333

2256-
If using Servlet 3, Java based configuration, e.g. via `WebApplicationInitializer`,
2334+
If using Servlet 3, Java based configuration for example via `WebApplicationInitializer`,
22572335
you'll also need to set the "asyncSupported" flag as well as the ASYNC dispatcher type
22582336
just like with `web.xml`. To simplify all this configuration, consider extending
22592337
`AbstractDispatcherServletInitializer` or
2260-
`AbstractAnnotationConfigDispatcherServletInitializer`, which automatically set those
2261-
options and make it very easy to register `Filter` instances.
2338+
`AbstractAnnotationConfigDispatcherServletInitializer` which automatically
2339+
set those options and make it very easy to register `Filter` instances.
22622340

22632341
[[mvc-ann-async-configuration-spring-mvc]]
2264-
===== Spring MVC Async Config
2265-
The MVC Java config and the MVC namespace both provide options for configuring async
2266-
request processing. `WebMvcConfigurer` has the method `configureAsyncSupport` while
2267-
<mvc:annotation-driven> has an <async-support> sub-element.
2342+
===== Spring MVC Configuration
2343+
2344+
The MVC Java config and the MVC namespace provide options for configuring
2345+
asynchronous request processing. `WebMvcConfigurer` has the method
2346+
`configureAsyncSupport` while `<mvc:annotation-driven>` has an
2347+
`<async-support>` sub-element.
22682348

22692349
Those allow you to configure the default timeout value to use for async requests, which
22702350
if not set depends on the underlying Servlet container (e.g. 10 seconds on Tomcat). You

0 commit comments

Comments
 (0)