本部分示例见这个项目的 mvc 分支下的 AsyncController.java
Spring MVC 3.2 中引进了基于异步请求处理的 Servlet 3。除了返回一个值,一个控制器方法现在可以返回一个java.util.concurrent.Callable
并生产来自 Spring MVC 管理的线程的返回值。同时主 Servlet 容器线程退出、释放并允许处理其他请求。Spring MVC 在 TaskExecutor 的帮助下,在一个独立的线程中调用 Callable,当 Callable 返回时,请求被发回 Servlet 容器,使用 Callable 的返回值继续执行。这里有一个这样的控制器方法的例子:
@PostMapping
public Callable<String> processUpload(final MultipartFile file)
{
return new Callable<String>()
{
public String call() throws Exception
{
// ...
return "someView";
}
};
}
这个控制器方法的另一个选择是返回一个DeferredResult
实例。这种情况下,返回值可以由任何线程产生,比如一个没有被 Spring MVC 管理的线程。比如,结果可能产生于外部事件的响应,比如一个 JMS 消息,一个定时任务等。这里有一个这样的控制器方法的例子:
@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes()
{
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Save the deferredResult somewhere..
return deferredResult;
}
// In some other thread...
deferredResult.setResult(data);
如果没有任何对 Servlet 3.0 中异步请求处理特性的了解的话,这可能很难理解。去了解一下会很有帮助。这里列出几个这个机制的基本事实:
通过
request.startAsync()
调用,一个 ServletRequest 可以被置为异步模式。这么做的主要影响是,Servlet,以及任何 Filter,可以退出,但是响应依旧会保持开放来允许之后完成处理过程。对
request.startAsync()
的调用返回 AsyncContext,AsyncContext 可以被用于异步处理之上的进一步控制。比如它提供了方法调度功能,这很像 Servlet API 中的 forward,但是它允许应用程序继续在 Servlet 容器线程中进行请求处理。ServletRequest 提供对当前 DispatcherType 的访问,DispatcherType 可以用于区别处理初始化请求、异步调度、forward 和其他调度器(dispatcher)类型。
有了上面的意识,下面是用于带有 Callable 的异步请求处理的事件序列:
控制器返回一个 Callable
Spring MVC 开始异步处理,并把 Callable 提交给一个 TaskExecutor 用于在一个单独的线程中处理
DispatcherServlet 和所有的 Filter 退出 Servlet 容器线程,但是响应保持打开
这个 Callable 产生一个结果,Spring MVC 把这个请求调回 Servlet 容器继续处理
DispatcherServlet 继续执行,并继续处理来自 Callable 的异步产生的结果
DeferredResult 的事件序列很相似,除了它应该由应用程序从任何线程中来产生异步结果:
控制器返回一个 DeferredResult,并把它保存在内存中的队列或者列表中用于访问
Spring MVC 开始异步处理
DispatcherServlet 和所有配置的 Filter 退出请求处理线程,但是响应依旧打开
应用程序设置来自一些线程中的 DeferredResult,Spring MVC 把请求调回 Servlet 容器
DispatcherServlet 再次被调用,并继续处理异步产生的结果
要了解更多使用异步请求处理的动机的背景,以及什么时候或为什么使用它,请读这个博客文章系列.
因为所学有限,这一部分并没有真正完成。这里先占坑,日后再行补充。