原文地址:http://blog.springsource.org/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/

前面的文章中我介绍了Servlet 3、Spring MVC 3.2中支持异步的新特性,并介绍了一些实时更新的技术背景。在这篇文章中,我将展示一些Spring
MVC 3.2新特性的技术细节,以及对Spring MVC request生命周期多方面的影响。

如果需要将Controller层的方法转变为异步方法,只要将方法的返回值类型改为Callable就可以了。例如,返回视图名String类型的方法,可以改为返回Callable<String>类型;返回ResponseEntity类型的方法,可以改为返回Callable<ResponseEntity>类型;其他的返回值类型都可以以此类推。

这种处理方式中,除了Controller层方法在另外一个线程中处理完成外,其他的工作方式没有发生任何变化。当方法改变成异步处理后,保持处理方式的简单非常重要。因为你会发现,今天我仅仅讲方法改为异步方式,但还是有很多相关问题需要考虑到。

示例代码:

GitHub上spring-mvc-showcase项目中的spring-mvc-async分支里,有很多Controller层异步方法的示例。

例如下面的@ResponseBody方法,其中返回了视图名String:

  1. @RequestMapping(value="/response/annotation", method=RequestMethod.GET)
  2. public @ResponseBody Callable<String> responseBody() {
  3. return new Callable<String>() {
  4. public String call() throws Exception {
  5. // Do some work..
  6. Thread.sleep(3000L);
  7. return "The String ResponseBody";
  8. }
  9. };
  10. }

以及下面的ResponseEntity方法:

  1. @RequestMapping(value="/response/entity/headers", method=RequestMethod.GET)
  2. public Callable<ResponseEntity<String>> responseEntityCustomHeaders() {
  3. return new Callable<ResponseEntity<String>>() {
  4. public ResponseEntity<String> call() throws Exception {
  5. // Do some work..
  6. Thread.sleep(3000L);
  7. HttpHeaders headers = new HttpHeaders();
  8. headers.setContentType(MediaType.TEXT_PLAIN);
  9. return new ResponseEntity<String>(
  10. "The String ResponseBody with custom header Content-Type=text/plain", headers, HttpStatus.OK);
  11. }
  12. };
  13. }

还有redirect类型的视图名方法

  1. @RequestMapping(value="/uriTemplate", method=RequestMethod.GET)
  2. public Callable<String> uriTemplate(final RedirectAttributes redirectAttrs) {
  3. return new Callable<String>() {
  4. public String call() throws Exception {
  5. // Do some work..
  6. Thread.sleep(3000L);
  7. redirectAttrs.addAttribute("account", "a123");  // Used as URI template variable
  8. redirectAttrs.addAttribute("date", new LocalDate(2011, 12, 31));  // Appended as a query parameter
  9. return "redirect:/redirect/{account}";
  10. }
  11. };
  12. }

添加了@RequestMapping注解和@ResponseBody注解的方法中,这些注解同样会应用到返回值Callable中。添加了@ExceptionHandler注解的方法也一样,它调用了Controller层方法返回的Callable中抛出的异常。

  1. package org.springframework.samples.mvc.exceptions;
  2. import java.util.concurrent.Callable;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.web.bind.annotation.ExceptionHandler;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. @Controller
  8. public class ExceptionController {
  9. @RequestMapping("/exception")
  10. public @ResponseBody Callable<String> exception() {
  11. return new Callable<String>() {
  12. public String call() throws Exception {
  13. // Do some work..
  14. Thread.sleep(2000L);
  15. throw new IllegalStateException("Sorry!");
  16. }
  17. };
  18. }
  19. @ExceptionHandler
  20. public @ResponseBody String handle(IllegalStateException e) {
  21. return "IllegalStateException handled!";
  22. }
  23. }

在GitHub中提交的这个版本,记录了其中全部更新的情况。

如果你运行了上面的任意一个方法,将会在控制台看到如下信息:

  1. 16:19:23 [http-bio-8080-exec-3] DispatcherServlet - DispatcherServlet with name 'appServlet' processing ...
  2. 16:19:23 [http-bio-8080-exec-3] RequestMappingHandlerMapping - Looking up handler method for path /views/html
  3. 16:19:23 [http-bio-8080-exec-3] RequestMappingHandlerMapping - Returning handler method ...
  4. 16:19:23 [http-bio-8080-exec-3] DispatcherServlet - Exiting request thread and leaving the response open
  5. 16:19:23 [SimpleAsyncTaskExecutor-1] DispatcherServlet - Resuming asynchronous processing of ...
  6. 16:19:26 [SimpleAsyncTaskExecutor-1] DispatcherServlet - Rendering view ...
  7. 16:19:26 [SimpleAsyncTaskExecutor-1] JstlView - Added model object 'fruit'
  8. 16:19:26 [SimpleAsyncTaskExecutor-1] JstlView - Added model object 'foo'
  9. 16:19:26 [SimpleAsyncTaskExecutor-1] JstlView - Forwarding to resource [/WEB-INF/views/views/html.jsp]
  10. 16:19:26 [SimpleAsyncTaskExecutor-1] DispatcherServlet - Successfully completed request
  11. 16:19:26 [SimpleAsyncTaskExecutor-1] AsyncExecutionChainRunnable - Completing async request processing

从上面的日志信息中可以看出,Servlet容器调用的线程马上就执行完了方法,而余下的处理内容则在3秒钟后由另外一个线程完成。除了这些意外,上面的日志信息与普通的请求处理信息是一样的。

线程池配置:

正如上面的日志信息所示,返回值Callable会默认调用SimpleAsyncTaskExecutor类来处理,这个类非常简单而且没有重用线程。而在实际的产品中,你将可能会需要使用AsyncTaskExecutor类来针对你所处的环境进行适当的配置,甚至有可能你已经有了一个配置好的AsyncTaskExecutor类。可以用RequestMappingHandlerAdapter类中的asyncTaskExecutor属性来引用它。

超时设定:

超时设定是我们需要考虑的非常重要的一个方面。因为Servlet容器可能会强制将一个超时的未完成的异步请求关闭,你可以通过RequestMappingHandlerAdapter类中的asyncRequestTimeout属性指定超时时间。如果不指定超时时间,超时的时间将取决于Servlet容器所设定的时间。在Tomcat中,这个超时时间被默认设定为10秒钟(Servlet容器调用的线程执行时就开始计时)。

在超时后仍然使用一个request或response的影响是不确定的。在实际使用中,Servlet容器将尝试重用request和response对象。这样一来,避免在超时后仍然使用request和response将变得非常重要。

事实上,没有方法可以检测request是否已经超时。但是Servlet API中,当请求超时或网络出现问题时,将提供一个声明式的回调函数。Spring MVC中自动注册了这个声明,因此可以得知,一对请求响应是否不应该被使用。

下面是执行过程中的事件序列:

  1. Controller层的方法返回一个Callable值
  2. Spring MVC在另外一个线程中调用这个Callable
  3. 请求处理的主线程执行完毕退出,超时计时开始
  4. Spring MVC声明一个超时设定
  5. 将response的状态设置为503(SERVICE_UNAVAILABLE)
  6. 一段时间过后,Callable执行完毕并返回值
  7. Spring MVC抛出StaleAsyncWebRequestException异常,而并不去处理它
  8. 异常被日志记录

如果想要完全理解上面的过程,可以参考其中涉及到的三个线程——请求处理开始的线程(Servlet容器调用的线程)、执行Callable方法的线程(异步线程)和Servlet容器向Spring MVC声明超时的线程。

异常:

异步处理中HandlerExceptionResolver类和异常处理的机制没有太多不同。当返回Callable之前发生了异常,处理方式与普通异常一样;当执行Callable方法的过程中产生异常,处理方式也与普通异常一样,只不过将在当前线程(异步线程)中处理,并将仍然返回未处理的500状态的response。

ThreadLocal属性:

Spring MVC的一些部分和Spring MVC应用程序可能会以来ThreadLocal存储来获取request、locale及其他。当以异步的方式执行Callable方法时,异步线程将拥有相同的ThreadLocal属性。

OpenSessionInViewFilter和OpenSessionInViewInterceptor也被更新为以透明的方式工作。而当Controller层的方法使用了@Transactional注解时,方法返回时就将完成事务,而不会扩展到执行Callable方法的内部。如果Callable需要处理事务,则需要委托(delegate)一个事务组件。

拦截器处理:

已注册的HandlerInterceptor实例将与异步请求协作工作。主要的区别是:preHandle在Servlet容器线程开始的时候调用,而postHandle和afterCompletion方法则在异步线程中调用。在大多数情况下不会出现问题,除非HandlerInterceptor设置并清除了ThreadLocal属性。需要如此做的拦截器可能实现了AsyncHandlerInterceptor接口,这个借口为异步请求的处理添加了生命周期。

Servlet过滤器:

一些过滤器将正常工作。而其他的过滤器将尝试在Servlet容器线程退出后执行后置处理(post-processing)。这样的过滤器需要进行一定的修改用来在异步线程中完成后置处理。所有的Spring过滤器都已经按照要求(按照异步请求处理的要求)进行修改,来与异步请求协同工作。但是第三方的过滤器是否能够在Spring MVC下正常处理异步请求,取决于这些过滤器的实现细节。

总结:

在我的下一篇文章中,我将使用一个基于接收外部事件(AMQP消息)的示例,将其使用传统轮询的方式修改为使用长轮询的方式,用来在浏览器中显示实时信息。

Spring MVC 3.2 技术预览(三):动手写一个异步Controller方法的更多相关文章

  1. (转)ASP.NET MVC 第五个预览版和表单提交场景

    转自:http://ourlife.blog.51cto.com/708821/296171 上个星期四,ASP.NET MVC开发团队发布了ASP.NET MVC框架的“第五个预览版”.你可以在这里 ...

  2. Windows 10 技术预览版9926 “未知源”引起系统休眠后自启的解决办法

    问题的由来: 自从安装上了最新发布的Windows 10 ,使用起来有诸多的改进:无论是重绘的图标还是通知消息中心的整合还是更智能的OneDrive客户端都使得工作起来非常愉悦. 不过笔者这两天频繁遇 ...

  3. 新的理念、 新的解决方案、 新的Azure Stack技术预览

    Jeffrey Snover 我们很高兴地宣布︰Azure Stack Technical Preview 2(TP2)已发布!我们朝着向您的数据中心提供Azure服务能力的目标又更近一步.自发布第一 ...

  4. Windows 10 技术预览

    windows10的技术预览版已经发布了很久了,正式版大约在今年的夏天就会发布,作为微软寄予厚望的下一代全平台操作系统,相比于windows8.1,windows10做了哪些改进,又添加了哪些新功能. ...

  5. 微软推出首个Microsoft Azure Stack技术预览版

    Mike Neil,微软公司企业云副总裁 怀着对于提高业务灵活性.加速创新的期待,很多企业正在向云平台迅速迁移.伴随着这样的趋势,我们也见证了微软智能云Azure业务在全球市场的快速增长--每个月近1 ...

  6. 熊猫猪新系统测试之一:Windows 10 技术预览版

    话说本猫不用windows很多年了呀!不过看到微软最新的Windows10还是手痒了,想安装体验一把.于是第一时间下载,并做成usb引导安装镜像,在08年的老台式机上安装尝鲜鸟.下载ISO和安装方法这 ...

  7. 06、Windows 10 技术预览

    随着 Windows 10 发布的,未来 Windows 平台都是统一开发模型,可以只写一个 Appx 包,就可以同时部署到 Windows/ Windowsw Phone/ Tablet /xbox ...

  8. 微软发布Azure Stack第一个技术预览版

    为了提升商业灵敏度和加快创新步伐,各个企业都在迅速地转向云服务.在微软,我们已经见到微软智能云Azure的飞速发展和使用,每月我们都有近十万的新增订阅量.然而,我们也了解到还有很多企业在完全移到公有云 ...

  9. 第07章-Spring MVC 的高级技术

    Spring MVC 的高级技术 1. Spring MVC配置的替代方案 1.1 自定义DispatcherServlet配置 AbstractAnnotationConfigDispatcherS ...

  10. 熊猫猪新系统測试之中的一个:Windows 10 技术预览版

    话说本猫不用windows非常多年了呀! 只是看到微软最新的Windows10还是手痒了.想安装体验一把. 于是第一时间下载,并做成usb引导安装镜像,在08年的老台式机上安装尝鲜鸟.下载ISO和安装 ...

随机推荐

  1. C#项目—彩票选号

    C#彩票选号软件 今天做了一个彩票选号的小软件,将学到的知识点总结如下(新手小白,多提意见): 1.写程序的思路 实体类(属性.方法) No1. 随机数组集合(属性) No2. 创建集合对象(构造方法 ...

  2. FFmpeg开发笔记(五十一)适合学习研究的几个音视频开源框架

    ​很多程序员想学习音视频的编程开发,却不知从何学习,因为音视频技术的体系庞大.知识杂糅,一眼望去就令人生怯.那么学习音视频建议站在前人的肩膀上,从优秀的音视频开源框架开始钻研,先熟悉这些开源工具的具体 ...

  3. GANF: 用于多时间序列异常检测的图增广归一化流《GRAPH-AUGMENTED NORMALIZING FLOWS FOR ANOMALY DETECTION OF MULTIPLE TIME SERIES》(异常检测、多时间序列、DAG、贝叶斯网络、归一化流)

    今天是2022年7月12日,差不多小半个月没看论文了,因为模型一直运行不起来+系统搭建的一塌糊涂,今天干脆摆烂,本咸鱼要去看新的论文了(逃避旧工作,bushi). 对了,我们放暑假了,可是我没放暑假. ...

  4. TypeScript 高级教程 – 把 TypeScript 当强类型语言使用 (第一篇)

    前言 原本是想照着 TypeScript 官网 handbook 写个教程的. 但提不起那个劲... 所以呢, 还是用我自己的方式写个复习和进阶笔记就好了呗. 以前写过的 TypeScript 笔记: ...

  5. ASP.NET Core Library – Excel 读写

    前言 以前写过 EPPlus 的笔记, 但后来 EPPlus 开始收费了.... (这好像是 .NET 生态的宿命) 在找替代方案中看中了微软的 Open XML SDK. 但经过一番折腾, 它确实太 ...

  6. MybatisPlus——入门案例

    MyBatisPlus MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发.提高效率 开发方式 基于MyBatis使用MyBatisPlus 基于Sprin ...

  7. SpringBoot——基础配置

    基础配置 配置格式 SpringBoot提供了多种属性配置方法 application.properties server.port=80 application.yml server: port: ...

  8. SelMatch:最新数据集蒸馏,仅用5%训练数据也是可以的 | ICML'24

    数据集蒸馏旨在从大型数据集中合成每类(IPC)少量图像,以在最小性能损失的情况下近似完整数据集训练.尽管在非常小的IPC范围内有效,但随着IPC增加,许多蒸馏方法变得不太有效甚至性能不如随机样本选择. ...

  9. 树莓派2 CentOS7.9 安装配置笔记

    1. 镜像下载与安装 http://isoredirect.centos.org/altarch/7/isos/armhfp/找到https://mirrors.tuna.tsinghua.edu.c ...

  10. 65.说下vue3的使用感想(说些vue3对比vue3的方便之处)

    vue3 使用了组合式API,setup 替换了选项式api ,不需要在多个api里面写代码了,而且使用了setup的语法糖,可以更加方便写代码 : vue3使用proxy替代了Object.defi ...