Skip to content

Commit e09bdb3

Browse files
committed
Add reference documentation for async web requests
Issue: SPR-9400
1 parent 5b2cd76 commit e09bdb3

File tree

2 files changed

+238
-4
lines changed

2 files changed

+238
-4
lines changed

src/reference/docbook/mvc.xml

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,18 @@ public String processSubmit(<emphasis role="bold">@ModelAttribute("pet") Pet pet
15671567
linkend="mvc-ann-httpentity" />.</para>
15681568
</listitem>
15691569

1570+
<listitem>
1571+
<para>A <interfacename>Callable&lt;?&gt;</interfacename> can
1572+
be returned when the application wants to produce the return
1573+
value asynchronously in a thread managed by Spring MVC.</para>
1574+
</listitem>
1575+
1576+
<listitem>
1577+
<para>A <classname>DeferredResult&lt;?&gt;</classname> can
1578+
be returned when the application wants to produce the return
1579+
value from a thread of its own choosing.</para>
1580+
</listitem>
1581+
15701582
<listitem>
15711583
<para>Any other return type is considered to be a single model
15721584
attribute to be exposed to the view, using the attribute name
@@ -1576,6 +1588,7 @@ public String processSubmit(<emphasis role="bold">@ModelAttribute("pet") Pet pet
15761588
objects and the results of <literal>@ModelAttribute</literal>
15771589
annotated reference data accessor methods.</para>
15781590
</listitem>
1591+
15791592
</itemizedlist></para>
15801593
</section>
15811594

@@ -2359,6 +2372,228 @@ public String myHandleMethod(WebRequest webRequest, Model model) {
23592372
</section>
23602373
</section>
23612374

2375+
<section xml:id="mvc-ann-async">
2376+
<title>Asynchronous Request Processing</title>
2377+
2378+
<para>Spring MVC 3.2 introduced Servlet 3 based asynchronous request
2379+
processing. Instead of returning a value, as usual, a controller method
2380+
can now return a <interfacename>java.util.concurrent.Callable</interfacename>
2381+
and produce the return value from a separate thread. Meanwhile the main Servlet
2382+
container thread is released and allowed to process other requests.
2383+
Spring MVC invokes the <interfacename>Callable</interfacename> in a
2384+
separate thread with the help of a <interfacename>TaskExecutor</interfacename>
2385+
and when the <interfacename>Callable</interfacename> returns, the
2386+
request is dispatched back to the Servlet container to resume
2387+
processing with the value returned by the
2388+
<interfacename>Callable</interfacename>.
2389+
Here is an example controller method:</para>
2390+
2391+
<programlisting language="java">
2392+
@RequestMapping(method=RequestMethod.POST)
2393+
public Callable&lt;String&gt; processUpload(final MultipartFile file) {
2394+
2395+
return new Callable&lt;String&gt;() {
2396+
public Object call() throws Exception {
2397+
// ...
2398+
return "someView";
2399+
}
2400+
};
2401+
}</programlisting>
2402+
2403+
<para>A second option is for the controller to return an instance of
2404+
<classname>DeferredResult</classname>. In this case the return value
2405+
will also be produced from a separate thread. However, that thread is not
2406+
known to Spring MVC. For example the result may be produced in response
2407+
to some external event such as a JMS message, a scheduled task, etc.
2408+
Here is an example controller method:</para>
2409+
2410+
<programlisting language="java">
2411+
@RequestMapping("/quotes")
2412+
@ResponseBody
2413+
public DeferredResult&lt;String&gt; quotes() {
2414+
DeferredResult&lt;String&gt; deferredResult = new DeferredResult&lt;String&gt;();
2415+
// Save the deferredResult in in-memory queue ...
2416+
return deferredResult;
2417+
}
2418+
2419+
// In some other thread...
2420+
deferredResult.setResult(data);
2421+
</programlisting>
2422+
2423+
<para>This may be difficult to understand without any knowledge of the
2424+
Servlet 3 async processing feature. It would certainly help to read up on it.
2425+
At a very minimum consider the following basic facts:</para>
2426+
2427+
<itemizedlist>
2428+
<listitem>
2429+
<para>A <interfacename>ServletRequest</interfacename>
2430+
can be put in asynchronous mode by calling
2431+
<code>request.startAsync()</code>. The main effect of doing so is
2432+
that the Servlet, as well as any Filters, can exit but the response
2433+
will remain open allowing some other thread to complete processing.
2434+
</para>
2435+
</listitem>
2436+
<listitem>
2437+
<para>The call to <code>request.startAsync()</code> returns an
2438+
<interfacename>AsyncContext</interfacename>, which can be used for
2439+
further control over async processing. For example it provides
2440+
the method <code>dispatch</code>, which can be called from an
2441+
application thread in order to "dispatch" the request back to
2442+
the Servlet container. An async dispatch is similar to a forward
2443+
except it is made from one (application) thread to another
2444+
(Servlet container) thread whereas a forward occurs synchronously
2445+
in the same (Servlet container) thread.</para>
2446+
</listitem>
2447+
<listitem>
2448+
<para><interfacename>ServletRequest</interfacename> provides access
2449+
to the current <interfacename>DispatcherType</interfacename>, which
2450+
can be used to distinguish if a <interfacename>Servlet</interfacename> or
2451+
a <interfacename>Filter</interfacename> is processing on
2452+
the initial request processing thread and when it is processing in
2453+
an async dispatch.</para>
2454+
</listitem>
2455+
</itemizedlist>
2456+
2457+
<para>With the above in mind, the following is the sequence
2458+
of events for async request processing with a <interfacename>Callable</interfacename>:
2459+
(1) Controller returns a
2460+
<interfacename>Callable</interfacename>, (2) Spring MVC starts async processing
2461+
and submits the <interfacename>Callable</interfacename>
2462+
to a <interfacename>TaskExecutor</interfacename>
2463+
for processing in a separate thread, (3) the <classname>DispatcherServlet</classname>
2464+
and all Filter's exit the request processing thread but the response
2465+
remains open, (4) the <interfacename>Callable</interfacename> produces a result
2466+
and Spring MVC dispatches the request back to the Servlet container,
2467+
(5) the <classname>DispatcherServlet</classname> is invoked again and processing
2468+
resumes with the asynchronously produced result from the
2469+
<interfacename>Callable</interfacename>. The exact sequencing of (2),
2470+
(3), and (4) may vary depending on the speed of execution of the
2471+
concurrent threads.
2472+
</para>
2473+
2474+
<para>The sequence of events for async request processing with a
2475+
<classname>DeferredResult</classname> is the same in principal except
2476+
it's up to the application to produce the asynchronous result from some thread:
2477+
(1) Controller returns a <classname>DeferredResult</classname> and saves it
2478+
in some in-memory queue or list where it can be accessed,
2479+
(2) Spring MVC starts async processing, (3) the <classname>DispatcherServlet</classname>
2480+
and all configured Filter's exit the request processing thread but the response
2481+
remains open, (4) the application sets the <classname>DeferredResult</classname>
2482+
from some thread and Spring MVC dispatches the request back to the Servlet container,
2483+
(5) the <classname>DispatcherServlet</classname> is invoked again and processing
2484+
resumes with the asynchronously produced result.
2485+
</para>
2486+
2487+
<para>Explaining the motivation for async request processing, when or why to use it
2488+
is beyond the scope of this document. For further information you may wish to read
2489+
<link xl:href="http://http://blog.springsource.org/2012/05/06/spring-mvc-3-2-preview-introducing-servlet-3-async-support/"> this blog post series</link>.
2490+
</para>
2491+
2492+
<section xml:id="mvc-ann-async-exceptions">
2493+
<title>Async Request Processing and Exception Handling</title>
2494+
2495+
<para>What happens if a <interfacename>Callable</interfacename> returned
2496+
from a controller method raises an Exception while being executed?
2497+
The effect is similar to what happens when any controller method raises
2498+
an exception. It is handled by a matching
2499+
<interfacename>@ExceptionHandler</interfacename> method in the same
2500+
controller or by one of the configured
2501+
<interfacename>HandlerExceptionResolver</interfacename> instances.</para>
2502+
2503+
<note>
2504+
<para>Under the covers, when a <interfacename>Callable</interfacename>
2505+
raises an Exception, Spring MVC still dispatches to the Servlet
2506+
container to resume processing. The only difference is that the
2507+
result of executing the <interfacename>Callable</interfacename>
2508+
is an <classname>Exception</classname> that must be processed
2509+
with the configured
2510+
<interfacename>HandlerExceptionResolver</interfacename> instances.</para>
2511+
</note>
2512+
2513+
<para>When using a <classname>DeferredResult</classname>, you have
2514+
a choice of calling its <code>setErrorResult(Object)</code> method
2515+
and provide an <classname>Exception</classname> or any other Object
2516+
you'd like to use as the result. If the result is an
2517+
<classname>Exception</classname>, it will be processed with a
2518+
matching <interfacename>@ExceptionHandler</interfacename> method in the
2519+
same controller or with any configured
2520+
<interfacename>HandlerExceptionResolver</interfacename> instance.</para>
2521+
</section>
2522+
2523+
<section xml:id="mvc-ann-async-interception">
2524+
<title>Intercepting Async Requests</title>
2525+
2526+
<para>An existing <interfacename>HandlerInterceptor</interfacename> can
2527+
implement <interfacename>AsyncHandlerInterceptor</interfacename>, which
2528+
provides one additional method <code>afterConcurrentHandlingStarted</code>.
2529+
It is invoked after async processing starts and when the initial
2530+
request processing thread is being exited. See the Javadoc of
2531+
<interfacename>AsyncHandlerInterceptor</interfacename> for more details
2532+
on that.</para>
2533+
2534+
<para>Further options for async request lifecycle callbacks are
2535+
provided directly on <classname>DeferredResult</classname>,
2536+
which has the methods <code>onTimeout(Runnable)</code> and
2537+
<code>onCompletion(Runnable)</code>. Those are called when the
2538+
async request is about to time out or has completed respectively.
2539+
The timeout event can be handled by setting the
2540+
<classname>DeferredResult</classname> to some value.
2541+
The completion callback however is final and the result can no
2542+
longer be set.</para>
2543+
2544+
<para>Similar callbacks are also available with a
2545+
<interfacename>Callable</interfacename>. However, you will need to wrap
2546+
the <interfacename>Callable</interfacename> in an instance of
2547+
<classname>WebAsyncTask</classname> and then use that to register
2548+
the timeout and completion callbacks. Just like with
2549+
<classname>DeferredResult</classname>, the timeout event can be
2550+
handled and a value can be returned while the completion event is final.</para>
2551+
2552+
<para>You can also register a
2553+
<interfacename>CallableProcessingInterceptor</interfacename> or a
2554+
<interfacename>DeferredResultProcessingInterceptor</interfacename>
2555+
globally through the MVC Java config or the MVC namespace.
2556+
Those interceptors provide a full set of callbacks and apply every
2557+
time a <interfacename>Callable</interfacename> or a
2558+
<classname>DeferredResult</classname> is used.</para>
2559+
2560+
</section>
2561+
2562+
<section xml:id="mvc-ann-async-configuration">
2563+
<title>Async Request Configuration</title>
2564+
2565+
<para>The MVC Java config and the MVC namespace provide options for
2566+
configuring async request processing.
2567+
<interfacename>WebMvcConfigurer</interfacename> has the method
2568+
<code>configureAsyncSupport</code> while &lt;mvc:annotation-driven&gt;
2569+
has an &lt;async-support&gt; sub-element.</para>
2570+
2571+
<para>Those allow you to configure the default timeout value to use for
2572+
async requests, which if not set depends on the underlying Servlet
2573+
container (e.g. 10 seconds on Tomcat). You can also configure an
2574+
<interfacename>AsyncTaskExecutor</interfacename> to use for executing
2575+
<interfacename>Callable</interfacename> instances returned from
2576+
controller methods. It is highly recommended to configure this property
2577+
since by default Spring MVC uses
2578+
<classname>SimpleAsyncTaskExecutor</classname>. The MVC Java config
2579+
and the MVC namespace also allow you to register
2580+
<interfacename>CallableProcessingInterceptor</interfacename> and
2581+
<interfacename>DeferredResultProcessingInterceptor</interfacename>
2582+
instances.</para>
2583+
2584+
<para>If you need to override the default timeout value for a
2585+
specific <classname>DeferredResult</classname>, you can do so by using
2586+
the appropriate class constructor. Similarly, for a
2587+
<interfacename>Callable</interfacename>, you can wrap it in a
2588+
<classname>WebAsyncTask</classname> and use the appropriate class
2589+
constructor to customize the timeout value. The class constructor of
2590+
<classname>WebAsyncTask</classname> also allows providing
2591+
an <interfacename>AsyncTaskExecutor</interfacename>.</para>
2592+
2593+
</section>
2594+
2595+
</section>
2596+
23622597
<section xml:id="mvc-ann-tests">
23632598
<title>Testing Controllers</title>
23642599

src/reference/docbook/new-in-3.2.xml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,8 @@
3737
</listitem>
3838
</itemizedlist>
3939

40-
<para>See <link
41-
xl:href="http://blog.springsource.org/2012/05/06/spring-mvc-3-2-preview-introducing-servlet-3-async-support/">
42-
Introducing Servlet 3 Async Support</link> (SpringSource team
43-
blog).</para>
40+
<para>See <xref linkend="mvc-ann-async"/>.</para>
41+
4442
</section>
4543

4644
<section xml:id="new-in-3.2-spring-mvc-test">
@@ -258,5 +256,6 @@
258256
linkend="testcontext-ctx-management-initializers">ApplicationContextInitializers</link></para>
259257
</listitem>
260258
</itemizedlist>
259+
261260
</section>
262261
</chapter>

0 commit comments

Comments
 (0)