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的特性,这可能有点难以理解.这肯定会帮助阅读.这里有一些关于底层机制基本原理:

1、Servlet 3.0 异步机制

  1. 一个ServletRequest可以通过调用 request.startAsync()被放到一个异步的模块中.这样做的主要作用是Servlet,以及任何过滤器,可以退出,但是响应仍将允许开放直到处理完成后。
  2. 调用request.startAsync()返回AsyncContext可以用于进一步控制异步处理。例如,它提供一个方法叫dispatch,这个类似于Servlet API,并且它允许应用程序通过Servlet容器线程继续请求处理。
  3. ServletRequest提供获取当前DispatcherType的接口.DispatcherType可以用来区分处理初始请求,一个异步分发,forward,以及其他分发类型。

记住上面的,下面是通过Callable异步请求处理事件的序列:

  1. Controller返回一个Callable对象.
  2. Spring MVC开始异步处理并且提交CallableTaskExecutor在一个单独的线程中进行处理。
  3. DispatcherServlet与所有的Filter的Servlet容器线程退出,但Response仍然开放。
  4. Callable产生结果并且Spring MVC分发请求给Servlet容器继续处理.
  5. DispatcherServlet再次被调用并且继续异步的处理由Callable产生的结果

DeferredResult的处理顺序与Callable十分相似,由应用程序多线程产生异步结果:

  1. Controller返回一个DeferredResult对象,并且把它保存在内在队列当中或者可以访问它的列表中。
  2. Spring MVC开始异步处理.
  3. DispatcherServlet与所有的Filter的Servlet容器线程退出,但Response仍然开放。
  4. application通过多线程返回DeferredResult中sets值.并且Spring MVC分发request给Servlet容器.
  5. DispatcherServlet再次被调用并且继续异步的处理产生的结果.

为进一步在异步请求处理动机的背景,并且when或者why使用它请看this blog post series.

2、异步请求的异常处理

Callable执行一个Controller方法的时候带有异常怎么办?简单的回答就是:和当执行一个普通Controller方法带有异常一样.它通过有规律的异常处理机制。详细点来说就是:当Callable带有异常时,Spring MVC以这个Exception为结果分发给Servlet容器.并且引导带有Exception处理request而不是Controller方法返回一个值。当使用DeferredResult时,你可以选择是否把Exception实例通过调用setResult或者setErrorResult进行传值.

3、拦截异步请求

一个HandlerInterceptor可样也可以实现AsyncHandlerInterceptor,通过实现它的afterConcurrentHandlingStarted方法进行回调.当进行异步处理的时候,将会调用afterConcurrentHandlingStarted而不是postHandleafterCompletion.

一个HandlerInterceptor同样可以注册CallableProcessingInterceptor或者一个DeferredResultProcessingInterceptor用于更深度的集成异步request的生命周期.例如处理一个timeout事件.你可以参看AsyncHandlerInterceptor的Javadoc了解更多细节.

DeferredResult类也提供了方法,像onTimeout(Runnable)onCompletion(Runnable).可以看DeferredResult了解更多细节.

当使用Callable的时候,你可以通过WebAsyncTask来对它进行包装.这样也可以提供注册timeout与completion方法.

4、HTTP Streaming

一个Controller方法可以通过使用DeferredResultCallable来异步的产生它的返回值.并且这个可以被用来实现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.

5、HTTP Streaming With Server-Sent Events

SseEmitterResponseBodyEmitter的子类,它提供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了解更多.

6、HTTP Streaming Directly To The OutputStream

ResponseBodyEmitter允许通过HttpMessageConverter把发送的events写到对象到response中.这可能是最常见的情况。例如写JSON数据.可是有时候它被用来绕开message转换直接写入到response的OutputStream。例如文件下载.这样可以通过返回StreamingResponseBody类型的值做到.

下面就是一个简单的例子:

@RequestMapping("/download")
public StreamingResponseBody handle() {return new StreamingResponseBody() {@Overridepublic void writeTo(OutputStream outputStream) throws IOException {// write...}};
}

注意:StreamingResponseBody同样可以用来作为ResponseEntity中的body用来定制化response的状态与headers。

7、配置异步请求

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/javaeehttp://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/javaeehttp://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允许你注册CallableProcessingInterceptorDeferredResultProcessingInterceptor实例.

如果你想覆盖DeferredResult的默认过期时间,你可以选择使用合适的构造器.同样的,对于Callable,你可以通过WebAsyncTask来包装它并且使用相应的构造器来定制化过期时间.WebAsyncTask的构造器同样允许你提供一个AsyncTaskExecutor.

因为水平有限,翻译不足之处还望见谅。
原文地址:spring-framework-reference-4.2.6.RELEASE

7、Spring MVC 之 处理异步请求相关推荐

  1. spring mvc 异步_DeferredResult – Spring MVC中的异步处理

    spring mvc 异步 DeferredResult是一个可能尚未完成的计算的容器,它将在将来提供. Spring MVC使用它来表示异步计算,并利用Servlet 3.0 AsyncContex ...

  2. DeferredResult – Spring MVC中的异步处理

    DeferredResult是一个可能尚未完成的计算的容器,它将在将来提供. Spring MVC使用它来表示异步计算,并利用Servlet 3.0 AsyncContext异步请求处理. 简要介绍一 ...

  3. Spring MVC 原理探秘 - 一个请求的旅行过程

    1.简介 在前面的文章中,我较为详细的分析了 Spring IOC 和 AOP 部分的源码,并写成了文章.为了让我的 Spring 源码分析系列文章更为丰富一些,所以从本篇文章开始,我将来向大家介绍一 ...

  4. Spring MVC能响应HTTP请求的原因?

    很多Java面试官喜欢问这个问题: 一个Spring MVC的项目文件里,开发人员没有开发自己的Servlet,只通过注解@RequestMapping定义了方法home能响应发向 /mvc/test ...

  5. Spring MVC 执行过程原理(请求映射原理、参数处理原理、返回值处理器)

    Spring MVC 执行过程分析 文章目录 Spring MVC 执行过程分析 请求映射原理 适配器Adapter 执行目标方法 参数处理器解析器HandlerMethodArgumentResol ...

  6. spring mvc 和ajax异步交互完整实例

    2019独角兽企业重金招聘Python工程师标准>>> Spring MVC 异步交互demo: 1.jsp页面: <%@ page language="java&q ...

  7. jQuery中Ajax+Spring MVC实现跨域请求

    项目开发中,某个可独立.也可集成的子业务模块须要向外开放相关API接口,先说下项目本身使用了jersery来实现RESTful webservice以名词形式公布API.有意思的是在实际的操作中同事却 ...

  8. 0069 如何在Intellij IDEA中查看Spring MVC项目的所有请求与处理方法的映射列表

    @RequestMapping注解分散于Controller中,如何在一个列表中看到所有的请求呢,比如下面这样: File-->Project Structure-->Modules--& ...

  9. java中的 请求体_在spring mvc test中访问请求体和请求头

    我创建了一个spring boot应用程序,这就是我的控制器的样子 . 我使用postman在请求体中发送json,在请求头中发送一个字符串,然后进一步散列json并将其与请求头获取的字符串进行比较 ...

最新文章

  1. ef mysql6.0 5.6_Win10+VS2015+EF6.0+MySQL5.6+MVC环境部署和排错
  2. 倒计时三天丨NeurIPS 2020预讲会:7位智源青年科学家,21场报告
  3. C语言实现链式栈(LinkStack)
  4. docker 推送镜像到私有地址
  5. 前端程序员的一些有学习借鉴作用的网站
  6. Mac系统下安装Homebrew后无法使用brew命令
  7. IDEA基于kotlin开发android程序配置小结
  8. lamp php5.5,CentOS 5.5快速搭建Apache+PHP5+MySQL完美Web服务器(LAMP)
  9. Redux Todos Example
  10. LeetCode 470. 用 Rand7() 实现 Rand10()(随机概率)
  11. url中隐藏php后缀,url中如何隐藏.php
  12. Vue报错:sockjs.js?9be2:1627 GET http://192.168.43.88:8080/sockjs-node/info?t=1631603986586 net::ERR_CO
  13. log.py——打印出独立IP,并统计独立IP数
  14. 监控服务器的日志文件,服务器上监控日志文件
  15. cas 4.0单点登录服务端部署
  16. 【朝花夕拾】【编程基础】一 存储单位
  17. 我的网名--荡涤心灵
  18. windows电脑如何设置定时关机?电脑设置定时关机的方法
  19. 马上6等待服务器响应,解决网页响应慢,等待时间过长,waiting(TTFB)时间过长...
  20. 「建模学习」游戏中的场景建模,原来是靠3D扫描建模技术完成?

热门文章

  1. 【算法知识】先验分布、后验分布、似然估计
  2. 动态SQL 模糊查询 联表查询
  3. C++:子类与父类构造与析构的调用时机
  4. 分布式跟踪系统(SpringCloudSluth+OpenZipkin)
  5. sql: substr函数用法
  6. 卫星轨道和两行数据TLE
  7. CF785C (1600)
  8. Matlab绘制ROC曲线并计算AUC面积
  9. 一篇文章通透理解序列号实现原理
  10. E4416A-单通道EMP-F功率计简单上手