Spring MVC 3.2开始引入Servlet 3中的基于异步的处理request.往常是返回一个值,而现在是一个Controller方法可以返回一个java.util.concurrent.Callable
对象和从Spring MVC的托管线程生产返回值.同时Servlet容器的主线程退出和释放,允许处理其他请求。Spring MVC通过TaskExecutor
的帮助调用Callable
在一个单独的线程。并且当这个Callable
返回时,这个rquest被分配回Servlet容器使用由Callable
的返回值继续处理。
这里有这样的Controller方法的一个例子:
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public String call() throws Exception {
// ...
return "someView";
}
};
}
Controller方法的另外一个选择是返回一个DeferredResult
的实例。在这种情况下,返回值也将由多线程产生.i.e. 一个不是由Spring MVC托管。例如可能产生的结果在应对一些外部事件,比如JMS消息,一个计划任务等等.这里有这样的Controller方法的一个例子:
@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的特性,这可能有点难以理解.这肯定会帮助阅读.这里有一些关于底层机制基本原理:
ServletRequest
可以通过调用 request.startAsync()
被放到一个异步的模块中.这样做的主要作用是Servlet,以及任何过滤器,可以退出,但是响应仍将允许开放直到处理完成后。request.startAsync()
返回AsyncContext
可以用于进一步控制异步处理。例如,它提供一个方法叫dispatch
,这个类似于Servlet API,并且它允许应用程序通过Servlet容器线程继续请求处理。ServletRequest
提供获取当前DispatcherType
的接口.DispatcherType可以用来区分处理初始请求,一个异步分发,forward,以及其他分发类型。记住上面的,下面是通过Callable
异步请求处理事件的序列:
Callable
对象.Callable
到TaskExecutor
在一个单独的线程中进行处理。DispatcherServlet
与所有的Filter的Servlet容器线程退出,但Response仍然开放。Callable
产生结果并且Spring MVC分发请求给Servlet容器继续处理.DispatcherServlet
再次被调用并且继续异步的处理由Callable产生的结果DeferredResult
的处理顺序与Callable十分相似,由应用程序多线程产生异步结果:
DeferredResult
对象,并且把它保存在内在队列当中或者可以访问它的列表中。DispatcherServlet
与所有的Filter的Servlet容器线程退出,但Response仍然开放。DeferredResult
中sets值.并且Spring MVC分发request给Servlet容器.DispatcherServlet
再次被调用并且继续异步的处理产生的结果.为进一步在异步请求处理动机的背景,并且when或者why使用它请看this blog post series.
当Callable
执行一个Controller方法的时候带有异常怎么办?简单的回答就是:和当执行一个普通Controller方法带有异常一样.它通过有规律的异常处理机制。详细点来说就是:当Callable
带有异常时,Spring MVC以这个Exception
为结果分发给Servlet容器.并且引导带有Exception
处理request而不是Controller方法返回一个值。当使用DeferredResult
时,你可以选择是否把Exception
实例通过调用setResult
或者setErrorResult
进行传值.
一个HandlerInterceptor
可样也可以实现AsyncHandlerInterceptor
,通过实现它的afterConcurrentHandlingStarted
方法进行回调.当进行异步处理的时候,将会调用afterConcurrentHandlingStarted
而不是postHandle
和afterCompletion
.
一个HandlerInterceptor
同样可以注册CallableProcessingInterceptor
或者一个DeferredResultProcessingInterceptor
用于更深度的集成异步request的生命周期.例如处理一个timeout事件.你可以参看AsyncHandlerInterceptor
的Javadoc了解更多细节.
DeferredResult
类也提供了方法,像onTimeout(Runnable)
和onCompletion(Runnable)
.可以看DeferredResult了解更多细节.
当使用Callable
的时候,你可以通过WebAsyncTask
来对它进行包装.这样也可以提供注册timeout与completion方法.
一个Controller方法可以通过使用DeferredResult
与Callable
来异步的产生它的返回值.并且这个可以被用来实现long polling技术.就是服务器可以推动事件尽快给客户端。
如果当你需要在一个HTTP response中放入多个event怎么办?这个技术需要”Long Polling”.这就是所谓的”Long Polling”.Spring MVC通过使用ResponseBodyEmitter
做为返回值来实现这种功能.这样可以被用来发送多个Object。而不是使用@ResponseBody
这样。这样每个对象都可以对应一种HttpMessageConverter
被写入到response中。
下面是一个简单的例子:
@RequestMapping("/events")
public ResponseBodyEmitter handle() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
// Save the emitter somewhere..
return emitter;
}
// In some other thread
emitter.send("Hello once");
// and again later on
emitter.send("Hello again");
// and done at some point
emitter.complete();
注意:ResponseBodyEmitter
同样也可以用做ResponseEntity
中的body,用于定制化response的status与headers.
SseEmitter
是ResponseBodyEmitter
的子类,它提供Server-Sent Events.服务器事件发送是”HTTP Streaming”的另一个变种技术.只是从服务器发送的事件按照W3C Server-Sent Events规范来的.
Server-Sent Events能够来用于它们的预期使用目的,就是从server发送events到clients.在Spring MVC中可以很容易的实现.仅仅需要返回一个SseEmitter
类型的值.
注意:IE浏览器并不支持Server-Sent Events并且对于更高级的web应用程序消息传递场景:例如在线游戏,在线协作,金融应用以及其它.最好考虑使用Spring的WebSocket来支持.它包含SockJS-style WebSocket的仿真可以被广泛的浏览器加高。(包含IE浏览器).在一个消息中心架构中通过发布-订阅模型也能进行更高级别的与客户消息传递模式进行交互.你可以通过看the following blog post了解更多.
ResponseBodyEmitter
允许通过HttpMessageConverter
把发送的events写到对象到response中.这可能是最常见的情况。例如写JSON数据.可是有时候它被用来绕开message转换直接写入到response的OutputStream
。例如文件下载.这样可以通过返回StreamingResponseBody
类型的值做到.
下面就是一个简单的例子:
@RequestMapping("/download")
public StreamingResponseBody handle() {
return new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// write...
}
};
}
注意:StreamingResponseBody
同样可以用来作为ResponseEntity
中的body用来定制化response的状态与headers。
7.1 Servlet容器配置
保证web.xml
中application的配置的版本是3.0:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
...
</web-app>
可以通过web.xml中的子元素<async-supported>true</async-supported>
使得DispatcherServlet
支持异步.此外的任何Filter
参与异步语法处理必须配置为支持ASYNC分派器类型。这样可以确保Spring Framework提供的所有filter都能够异步分发.自从它们继承了OncePerRequestFilter
之后.并且在runtime的时候会check filter是否需要被异步调用分发.
下面是web.xml的配置示例:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<filter>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.~.OpenEntityManagerInViewFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
</web-app>
如果使用Sevlet3,Java配置可以通过WebApplicationInitializer
,你同样需要像在web.xml
中一样,设置”asyncSupported”标签为ASYNC.为了简化这个配置,考虑继承AbstractDispatcherServletInitializer
或者AbstractAnnotationConfigDispatcherServletInitializer
。它们会自动设置这些选项,使它很容易注册过滤器实例。
7.2. Spring MVC配置
Spring MVC提供Java Config与MVC namespace作为选择用来配置处理异步request.WebMvcConfigurer可以通过configureAsyncSupport来进行配置,而xml可以通过子元素来进行配置.
如果你不想依赖Servlet容器(e.g. Tomcat是10)配置的值,允许你配置异步请求默认的timeout值。你可以配置AsyncTaskExecutor
用来包含Callable
实例作为controller方法的返回值.强烈建议配置这个属性,因为在默认情况下Spring MVC使用SimpleAsyncTaskExecutor
。Spring MVC中Java配置与namespace允许你注册CallableProcessingInterceptor
与DeferredResultProcessingInterceptor
实例.
如果你想覆盖DeferredResult
的默认过期时间,你可以选择使用合适的构造器.同样的,对于Callable
,你可以通过WebAsyncTask
来包装它并且使用相应的构造器来定制化过期时间.WebAsyncTask
的构造器同样允许你提供一个AsyncTaskExecutor
.
因为水平有限,翻译不足之处还望见谅。
原文地址:spring-framework-reference-4.2.6.RELEASE
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。