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>
|
linkend="mvc-ann-httpentity" />.</para>
|
||||||
</listitem>
|
</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>
|
<listitem>
|
||||||
<para>Any other return type is considered to be a single model
|
<para>Any other return type is considered to be a single model
|
||||||
attribute to be exposed to the view, using the attribute name
|
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>
|
objects and the results of <literal>@ModelAttribute</literal>
|
||||||
annotated reference data accessor methods.</para>
|
annotated reference data accessor methods.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
</itemizedlist></para>
|
</itemizedlist></para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -2359,6 +2372,228 @@ public String myHandleMethod(WebRequest webRequest, Model model) {
|
||||||
</section>
|
</section>
|
||||||
</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">
|
<section xml:id="mvc-ann-tests">
|
||||||
<title>Testing Controllers</title>
|
<title>Testing Controllers</title>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,8 @@
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
<para>See <link
|
<para>See <xref linkend="mvc-ann-async"/>.</para>
|
||||||
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>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section xml:id="new-in-3.2-spring-mvc-test">
|
<section xml:id="new-in-3.2-spring-mvc-test">
|
||||||
|
|
@ -258,5 +256,6 @@
|
||||||
linkend="testcontext-ctx-management-initializers">ApplicationContextInitializers</link></para>
|
linkend="testcontext-ctx-management-initializers">ApplicationContextInitializers</link></para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue