原文地址: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:
@RequestMapping(value="/response/annotation", method=RequestMethod.GET)
public @ResponseBody Callable<String> responseBody() {
return new Callable<String>() {
public String call() throws Exception {
// Do some work..
Thread.sleep(3000L);
return "The String ResponseBody";
}
};
}
以及下面的ResponseEntity方法:
@RequestMapping(value="/response/entity/headers", method=RequestMethod.GET)
public Callable<ResponseEntity<String>> responseEntityCustomHeaders() {
return new Callable<ResponseEntity<String>>() {
public ResponseEntity<String> call() throws Exception {
// Do some work..
Thread.sleep(3000L);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity<String>(
"The String ResponseBody with custom header Content-Type=text/plain", headers, HttpStatus.OK);
}
};
}
还有redirect类型的视图名方法:
@RequestMapping(value="/uriTemplate", method=RequestMethod.GET)
public Callable<String> uriTemplate(final RedirectAttributes redirectAttrs) {
return new Callable<String>() {
public String call() throws Exception {
// Do some work..
Thread.sleep(3000L);
redirectAttrs.addAttribute("account", "a123"); // Used as URI template variable
redirectAttrs.addAttribute("date", new LocalDate(2011, 12, 31)); // Appended as a query parameter
return "redirect:/redirect/{account}";
}
};
}
添加了@RequestMapping注解和@ResponseBody注解的方法中,这些注解同样会应用到返回值Callable中。添加了@ExceptionHandler注解的方法也一样,它调用了Controller层方法返回的Callable中抛出的异常。
package org.springframework.samples.mvc.exceptions;
import java.util.concurrent.Callable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ExceptionController {
@RequestMapping("/exception")
public @ResponseBody Callable<String> exception() {
return new Callable<String>() {
public String call() throws Exception {
// Do some work..
Thread.sleep(2000L);
throw new IllegalStateException("Sorry!");
}
};
}
@ExceptionHandler
public @ResponseBody String handle(IllegalStateException e) {
return "IllegalStateException handled!";
}
}
在GitHub中提交的这个版本,记录了其中全部更新的情况。
如果你运行了上面的任意一个方法,将会在控制台看到如下信息:
16:19:23 [http-bio-8080-exec-3] DispatcherServlet - DispatcherServlet with name 'appServlet' processing ...
16:19:23 [http-bio-8080-exec-3] RequestMappingHandlerMapping - Looking up handler method for path /views/html
16:19:23 [http-bio-8080-exec-3] RequestMappingHandlerMapping - Returning handler method ...
16:19:23 [http-bio-8080-exec-3] DispatcherServlet - Exiting request thread and leaving the response open
16:19:23 [SimpleAsyncTaskExecutor-1] DispatcherServlet - Resuming asynchronous processing of ...
16:19:26 [SimpleAsyncTaskExecutor-1] DispatcherServlet - Rendering view ...
16:19:26 [SimpleAsyncTaskExecutor-1] JstlView - Added model object 'fruit'
16:19:26 [SimpleAsyncTaskExecutor-1] JstlView - Added model object 'foo'
16:19:26 [SimpleAsyncTaskExecutor-1] JstlView - Forwarding to resource [/WEB-INF/views/views/html.jsp]
16:19:26 [SimpleAsyncTaskExecutor-1] DispatcherServlet - Successfully completed request
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中自动注册了这个声明,因此可以得知,一对请求响应是否不应该被使用。
下面是执行过程中的事件序列:
- Controller层的方法返回一个Callable值
- Spring MVC在另外一个线程中调用这个Callable
- 请求处理的主线程执行完毕退出,超时计时开始
- Spring MVC声明一个超时设定
- 将response的状态设置为503(SERVICE_UNAVAILABLE)
- 一段时间过后,Callable执行完毕并返回值
- Spring MVC抛出StaleAsyncWebRequestException异常,而并不去处理它
- 异常被日志记录
如果想要完全理解上面的过程,可以参考其中涉及到的三个线程——请求处理开始的线程(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是Spring框架的一个核心模块,专为构建Web应用程序而设计。在3.2版本中,它提供了许多增强的功能和优化,使得开发人员能够更高效、更灵活地处理Web请求和响应。本压缩包包含Spring MVC 3.2运行所需的所有...
首先,我们需要了解Spring MVC异步模式的基本概念。在传统的同步处理中,请求到达服务器后,服务器会分配一个线程来处理该请求,直到请求完成才会释放线程。而在异步模式下,服务器接收请求后不会立即返回结果,而是...
十六、Spring MVC关于写几个配置文件的说明:介绍了在Spring MVC项目中需要配置哪些文件以及这些文件的作用,包括DispatcherServlet的配置文件、web.xml配置等。 十七、Spring MVC如何取得Spring管理的bean:阐述了...
Spring MVC 是一个强大的Java Web开发框架,用于构建高效、可维护和模块化的Web应用程序。它在Spring框架的基础上,提供了一种优雅的方式来处理HTTP请求和响应。在这个“最全的Spring MVC注解例子”中,我们将深入...
Spring 3.2 版本是该框架的一个重要里程碑,它在前几个版本的基础上进行了许多改进和优化,旨在提高开发效率、可维护性和性能。下面我们将详细探讨Spring 3.2框架的核心特性、主要模块以及它们如何协同工作。 首先...
在Spring 3.2中,主要包含了四个关键模块:IOC(Inversion of Control,控制反转),AOP(Aspect Oriented Programming,面向切面编程),MVC(Model-View-Controller,模型-视图-控制器)以及DAO(Data Access ...
SSM(Spring MVC + Spring + MyBatis)是Java Web开发中常见的三层架构组合,而Spring MVC 3.2、Spring 3.2和MyBatis 3.11是这套框架组合的特定版本。本项目是将这些框架通过Maven进行整合,方便在MyEclipse环境中...
Spring MVC是Spring框架的一部分,它为构建基于Java的Web应用程序提供了一个模型-视图-控制器(MVC)架构。这本书深入浅出地介绍了如何有效地使用Spring MVC进行开发,以下是一些核心知识点的详细说明: 1. **...
- **使用@Controller 注解定义一个控制器**:通过这个注解可以将类标记为一个控制器,Spring 会自动检测并将其纳入管理。 - **使用@RequestMapping 注解映射请求路径**:此注解用于指定控制器处理的具体 URL 路径...
Spring MVC是Spring框架的一个核心模块,专为构建Web应用程序提供模型-视图-控制器(MVC)架构。在Spring MVC 4.0版本中,它引入了许多改进和新特性,以提升开发效率和应用程序的性能。 1. **依赖注入**:Spring ...
Spring MVC是Spring框架的一个核心模块,专为构建Web应用程序而设计。它提供了模型-视图-控制器(MVC)架构,使开发者能够有效地分离业务逻辑、数据处理和用户界面。在"Spring MVC 4.2.3"版本中,我们看到了一系列的...
Spring MVC 是一个基于Java的轻量级Web应用框架,它属于Spring框架的一部分,主要用于构建控制器层,实现模型-视图-控制器(Model-View-Controller)架构。在Spring MVC中,开发者可以方便地处理HTTP请求,进行数据...
Spring MVC 是一个强大的Java Web开发框架,它是Spring框架的一部分,专为构建高度可扩展和模块化的Web应用程序而设计。在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。...
Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。Spring MVC的设计目标是提供一个清晰的组件化架构,使得开发者可以独立地开发和测试控制器、...
Spring MVC 是一个强大的Java Web应用程序开发框架,是Spring框架的一部分,专注于处理Web请求和返回响应。它提供了模型-视图-控制器(MVC)架构,帮助开发者构建灵活、可维护的Web应用。在这个"spring mvc 整合包...
总结起来,这个"一个实用了spring mvc和ajax异步请求的例子"涵盖了Web开发中的重要概念,包括Spring MVC的Controller设计、RESTful API的实现以及Ajax的异步数据交互。这样的组合使得Web应用更加动态,用户体验更佳...
Spring MVC 是一款基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在Spring MVC中,开发者可以利用模型-视图-控制器(MVC)架构模式来分离业务逻辑、数据处理和...
1. **MVC 架构模式**:MVC 是一种设计模式,将应用程序分为三个主要组件:Model(模型)负责业务逻辑,View(视图)处理用户界面,Controller(控制器)协调模型和视图之间的交互。Spring MVC 通过 ...