Add reference documentation for async web requests
Issue: SPR-9400
This commit is contained in:
parent
5b2cd763cf
commit
e09bdb31cc
|
|
@ -1567,6 +1567,18 @@ public String processSubmit(<emphasis role="bold">@ModelAttribute("pet") Pet pet
|
|||
linkend="mvc-ann-httpentity" />.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>A <interfacename>Callable<?></interfacename> can
|
||||
be returned when the application wants to produce the return
|
||||
value asynchronously in a thread managed by Spring MVC.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>A <classname>DeferredResult<?></classname> can
|
||||
be returned when the application wants to produce the return
|
||||
value from a thread of its own choosing.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Any other return type is considered to be a single model
|
||||
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
|
|||
objects and the results of <literal>@ModelAttribute</literal>
|
||||
annotated reference data accessor methods.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist></para>
|
||||
</section>
|
||||
|
||||
|
|
@ -2359,6 +2372,228 @@ public String myHandleMethod(WebRequest webRequest, Model model) {
|
|||
</section>
|
||||
</section>
|
||||
|
||||
<section xml:id="mvc-ann-async">
|
||||
<title>Asynchronous Request Processing</title>
|
||||
|
||||
<para>Spring MVC 3.2 introduced Servlet 3 based asynchronous request
|
||||
processing. Instead of returning a value, as usual, a controller method
|
||||
can now return a <interfacename>java.util.concurrent.Callable</interfacename>
|
||||
and produce the return value from a separate thread. Meanwhile the main Servlet
|
||||
container thread is released and allowed to process other requests.
|
||||
Spring MVC invokes the <interfacename>Callable</interfacename> in a
|
||||
separate thread with the help of a <interfacename>TaskExecutor</interfacename>
|
||||
and when the <interfacename>Callable</interfacename> returns, the
|
||||
request is dispatched back to the Servlet container to resume
|
||||
processing with the value returned by the
|
||||
<interfacename>Callable</interfacename>.
|
||||
Here is an example controller method:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
@RequestMapping(method=RequestMethod.POST)
|
||||
public Callable<String> processUpload(final MultipartFile file) {
|
||||
|
||||
return new Callable<String>() {
|
||||
public Object call() throws Exception {
|
||||
// ...
|
||||
return "someView";
|
||||
}
|
||||
};
|
||||
}</programlisting>
|
||||
|
||||
<para>A second option is for the controller to return an instance of
|
||||
<classname>DeferredResult</classname>. In this case the return value
|
||||
will also be produced from a separate thread. However, that thread is not
|
||||
known to Spring MVC. For example the result may be produced in response
|
||||
to some external event such as a JMS message, a scheduled task, etc.
|
||||
Here is an example controller method:</para>
|
||||
|
||||
<programlisting language="java">
|
||||
@RequestMapping("/quotes")
|
||||
@ResponseBody
|
||||
public DeferredResult<String> quotes() {
|
||||
DeferredResult<String> deferredResult = new DeferredResult<String>();
|
||||
// Save the deferredResult in in-memory queue ...
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
// In some other thread...
|
||||
deferredResult.setResult(data);
|
||||
</programlisting>
|
||||
|
||||
<para>This may be difficult to understand without any knowledge of the
|
||||
Servlet 3 async processing feature. It would certainly help to read up on it.
|
||||
At a very minimum consider the following basic facts:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>A <interfacename>ServletRequest</interfacename>
|
||||
can be put in asynchronous mode by calling
|
||||
<code>request.startAsync()</code>. The main effect of doing so is
|
||||
that the Servlet, as well as any Filters, can exit but the response
|
||||
will remain open allowing some other thread to complete processing.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The call to <code>request.startAsync()</code> returns an
|
||||
<interfacename>AsyncContext</interfacename>, which can be used for
|
||||
further control over async processing. For example it provides
|
||||
the method <code>dispatch</code>, which can be called from an
|
||||
application thread in order to "dispatch" the request back to
|
||||
the Servlet container. An async dispatch is similar to a forward
|
||||
except it is made from one (application) thread to another
|
||||
(Servlet container) thread whereas a forward occurs synchronously
|
||||
in the same (Servlet container) thread.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><interfacename>ServletRequest</interfacename> provides access
|
||||
to the current <interfacename>DispatcherType</interfacename>, which
|
||||
can be used to distinguish if a <interfacename>Servlet</interfacename> or
|
||||
a <interfacename>Filter</interfacename> is processing on
|
||||
the initial request processing thread and when it is processing in
|
||||
an async dispatch.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>With the above in mind, the following is the sequence
|
||||
of events for async request processing with a <interfacename>Callable</interfacename>:
|
||||
(1) Controller returns a
|
||||
<interfacename>Callable</interfacename>, (2) Spring MVC starts async processing
|
||||
and submits the <interfacename>Callable</interfacename>
|
||||
to a <interfacename>TaskExecutor</interfacename>
|
||||
for processing in a separate thread, (3) the <classname>DispatcherServlet</classname>
|
||||
and all Filter's exit the request processing thread but the response
|
||||
remains open, (4) the <interfacename>Callable</interfacename> produces a result
|
||||
and Spring MVC dispatches the request back to the Servlet container,
|
||||
(5) the <classname>DispatcherServlet</classname> is invoked again and processing
|
||||
resumes with the asynchronously produced result from the
|
||||
<interfacename>Callable</interfacename>. The exact sequencing of (2),
|
||||
(3), and (4) may vary depending on the speed of execution of the
|
||||
concurrent threads.
|
||||
</para>
|
||||
|
||||
<para>The sequence of events for async request processing with a
|
||||
<classname>DeferredResult</classname> is the same in principal except
|
||||
it's up to the application to produce the asynchronous result from some thread:
|
||||
(1) Controller returns a <classname>DeferredResult</classname> and saves it
|
||||
in some in-memory queue or list where it can be accessed,
|
||||
(2) Spring MVC starts async processing, (3) the <classname>DispatcherServlet</classname>
|
||||
and all configured Filter's exit the request processing thread but the response
|
||||
remains open, (4) the application sets the <classname>DeferredResult</classname>
|
||||
from some thread and Spring MVC dispatches the request back to the Servlet container,
|
||||
(5) the <classname>DispatcherServlet</classname> is invoked again and processing
|
||||
resumes with the asynchronously produced result.
|
||||
</para>
|
||||
|
||||
<para>Explaining the motivation for async request processing, when or why to use it
|
||||
is beyond the scope of this document. For further information you may wish to read
|
||||
<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>.
|
||||
</para>
|
||||
|
||||
<section xml:id="mvc-ann-async-exceptions">
|
||||
<title>Async Request Processing and Exception Handling</title>
|
||||
|
||||
<para>What happens if a <interfacename>Callable</interfacename> returned
|
||||
from a controller method raises an Exception while being executed?
|
||||
The effect is similar to what happens when any controller method raises
|
||||
an exception. It is handled by a matching
|
||||
<interfacename>@ExceptionHandler</interfacename> method in the same
|
||||
controller or by one of the configured
|
||||
<interfacename>HandlerExceptionResolver</interfacename> instances.</para>
|
||||
|
||||
<note>
|
||||
<para>Under the covers, when a <interfacename>Callable</interfacename>
|
||||
raises an Exception, Spring MVC still dispatches to the Servlet
|
||||
container to resume processing. The only difference is that the
|
||||
result of executing the <interfacename>Callable</interfacename>
|
||||
is an <classname>Exception</classname> that must be processed
|
||||
with the configured
|
||||
<interfacename>HandlerExceptionResolver</interfacename> instances.</para>
|
||||
</note>
|
||||
|
||||
<para>When using a <classname>DeferredResult</classname>, you have
|
||||
a choice of calling its <code>setErrorResult(Object)</code> method
|
||||
and provide an <classname>Exception</classname> or any other Object
|
||||
you'd like to use as the result. If the result is an
|
||||
<classname>Exception</classname>, it will be processed with a
|
||||
matching <interfacename>@ExceptionHandler</interfacename> method in the
|
||||
same controller or with any configured
|
||||
<interfacename>HandlerExceptionResolver</interfacename> instance.</para>
|
||||
</section>
|
||||
|
||||
<section xml:id="mvc-ann-async-interception">
|
||||
<title>Intercepting Async Requests</title>
|
||||
|
||||
<para>An existing <interfacename>HandlerInterceptor</interfacename> can
|
||||
implement <interfacename>AsyncHandlerInterceptor</interfacename>, which
|
||||
provides one additional method <code>afterConcurrentHandlingStarted</code>.
|
||||
It is invoked after async processing starts and when the initial
|
||||
request processing thread is being exited. See the Javadoc of
|
||||
<interfacename>AsyncHandlerInterceptor</interfacename> for more details
|
||||
on that.</para>
|
||||
|
||||
<para>Further options for async request lifecycle callbacks are
|
||||
provided directly on <classname>DeferredResult</classname>,
|
||||
which has the methods <code>onTimeout(Runnable)</code> and
|
||||
<code>onCompletion(Runnable)</code>. Those are called when the
|
||||
async request is about to time out or has completed respectively.
|
||||
The timeout event can be handled by setting the
|
||||
<classname>DeferredResult</classname> to some value.
|
||||
The completion callback however is final and the result can no
|
||||
longer be set.</para>
|
||||
|
||||
<para>Similar callbacks are also available with a
|
||||
<interfacename>Callable</interfacename>. However, you will need to wrap
|
||||
the <interfacename>Callable</interfacename> in an instance of
|
||||
<classname>WebAsyncTask</classname> and then use that to register
|
||||
the timeout and completion callbacks. Just like with
|
||||
<classname>DeferredResult</classname>, the timeout event can be
|
||||
handled and a value can be returned while the completion event is final.</para>
|
||||
|
||||
<para>You can also register a
|
||||
<interfacename>CallableProcessingInterceptor</interfacename> or a
|
||||
<interfacename>DeferredResultProcessingInterceptor</interfacename>
|
||||
globally through the MVC Java config or the MVC namespace.
|
||||
Those interceptors provide a full set of callbacks and apply every
|
||||
time a <interfacename>Callable</interfacename> or a
|
||||
<classname>DeferredResult</classname> is used.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section xml:id="mvc-ann-async-configuration">
|
||||
<title>Async Request Configuration</title>
|
||||
|
||||
<para>The MVC Java config and the MVC namespace provide options for
|
||||
configuring async request processing.
|
||||
<interfacename>WebMvcConfigurer</interfacename> has the method
|
||||
<code>configureAsyncSupport</code> while <mvc:annotation-driven>
|
||||
has an <async-support> sub-element.</para>
|
||||
|
||||
<para>Those allow you to configure the default timeout value to use for
|
||||
async requests, which if not set depends on the underlying Servlet
|
||||
container (e.g. 10 seconds on Tomcat). You can also configure an
|
||||
<interfacename>AsyncTaskExecutor</interfacename> to use for executing
|
||||
<interfacename>Callable</interfacename> instances returned from
|
||||
controller methods. It is highly recommended to configure this property
|
||||
since by default Spring MVC uses
|
||||
<classname>SimpleAsyncTaskExecutor</classname>. The MVC Java config
|
||||
and the MVC namespace also allow you to register
|
||||
<interfacename>CallableProcessingInterceptor</interfacename> and
|
||||
<interfacename>DeferredResultProcessingInterceptor</interfacename>
|
||||
instances.</para>
|
||||
|
||||
<para>If you need to override the default timeout value for a
|
||||
specific <classname>DeferredResult</classname>, you can do so by using
|
||||
the appropriate class constructor. Similarly, for a
|
||||
<interfacename>Callable</interfacename>, you can wrap it in a
|
||||
<classname>WebAsyncTask</classname> and use the appropriate class
|
||||
constructor to customize the timeout value. The class constructor of
|
||||
<classname>WebAsyncTask</classname> also allows providing
|
||||
an <interfacename>AsyncTaskExecutor</interfacename>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section xml:id="mvc-ann-tests">
|
||||
<title>Testing Controllers</title>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,10 +37,8 @@
|
|||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>See <link
|
||||
xl:href="http://blog.springsource.org/2012/05/06/spring-mvc-3-2-preview-introducing-servlet-3-async-support/">
|
||||
Introducing Servlet 3 Async Support</link> (SpringSource team
|
||||
blog).</para>
|
||||
<para>See <xref linkend="mvc-ann-async"/>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section xml:id="new-in-3.2-spring-mvc-test">
|
||||
|
|
@ -258,5 +256,6 @@
|
|||
linkend="testcontext-ctx-management-initializers">ApplicationContextInitializers</link></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</chapter>
|
||||
|
|
|
|||
Loading…
Reference in New Issue