springMVC执行流程分析
springMVC是spring序列的一个web框架,现在已支持restful架构的风格,有取代struts2的趋势。本文只是将网上的一些文章进行总结归纳引用,便于回忆查找。
一.spring版本区别
- Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类。已经@Deprecated。
- Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类。
并且提供了一组强大的注解:
- @Controller:用于标识是处理器类;
- @RequestMapping:请求到处理器功能方法的映射规则;
- @RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;
- @ModelAttribute:请求参数到命令对象的绑定;
- @SessionAttributes:用于声明session级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session中;
- @InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;
注意:需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller 和@RequestMapping注解的处理器。- Spring3.0引入RESTful架构风格支持(通过@PathVariable注解和一些其他特性支持),且又引入了更多的注解支持:
- @CookieValue:cookie数据到处理器功能处理方法的方法参数上的绑定;
- @RequestHeader:请求头(header)数据到处理器功能处理方法的方法参数上的绑定;
- @RequestBody:请求的body体的绑定(通过HttpMessageConverter进行类型转换);
- @ResponseBody:处理器功能处理方法的返回值作为响应体(通过HttpMessageConverter进行类型转换);
- @ResponseStatus:定义处理器功能处理方法/异常处理器返回的状态码和原因;
- @ExceptionHandler:注解式声明异常处理器;
- @PathVariable:请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;
- Spring3.1使用新的HandlerMapping 和 HandlerAdapter来支持@Contoller和@RequestMapping注解处理器。
新的@Contoller和@RequestMapping注解支持类:处理器映射RequestMappingHandlerMapping和处理器适配器RequestMappingHandlerAdapter组合来代替Spring2.5开始的处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter,提供更多的扩展点。
配置了<mvc:annotation-driven/>
相当于自动加了一堆bean:
registers a RequestMappingHandlerMapping, a RequestMappingHandlerAdapter, and an ExceptionHandlerExceptionResolver (among others) in support of processing requests with annotated controller methods using annotations such as @RequestMapping, @ExceptionHandler, and others.
It also enables the following:
- Spring 3 style type conversion through a ConversionService instance in addition to the JavaBeans PropertyEditors used for Data Binding.
- Support for formatting Number fields using the @NumberFormat annotation through the ConversionService.
- Support for formatting Date, Calendar, Long, and Joda Time fields using the @DateTimeFormat annotation.
- Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath.
- HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values from @RequestMapping or @ExceptionHandler methods.
This is the complete list of HttpMessageConverters set up by mvc:annotation-driven:
- ByteArrayHttpMessageConverter converts byte arrays.
- StringHttpMessageConverter converts strings.
- ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.
- SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
- FormHttpMessageConverter converts form data to/from a MultiValueMap.
- Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present and Jackson 2 XML extension is not present on the classpath.
- MappingJackson2HttpMessageConverter converts to/from JSON — added if Jackson 2 is present on the classpath.
- MappingJackson2XmlHttpMessageConverter converts to/from XML — added if Jackson 2 XML extension is present on the classpath.
- AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.
- RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.
当然,也可以自己自定义各种配置,具体的方法可以参考springMVC docs;
二.springMVC执行流程
核心架构的具体流程步骤如下:
- 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
- DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
- DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
- HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
- ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
- View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
- 返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
三. 源码分析
处理的核心代码在DispatcherServlet的doDispatch方法中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
////检查是否是请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// //步骤2、请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//步骤3、处理器适配,即将我们的处理器包装成相应的适配器(从而支持多种类型的处理器)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//步骤4、由适配器执行处理器(调用处理器相应功能处理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
//执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//步骤5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))
//步骤6 视图在渲染时会把Model传入(view.render(mv.getModelInternal(), request, response);)
//HandlerInterceptor.afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
步骤4调用handle来真正执行响应的controller的方法,在此过程中还会调用convert进行类型转换等操作,若添加了<mvc:annotation-driven/>
的配置,如果maven引用了Jackson 2包,则会调用MappingJackson2HttpMessageConverter
这个converter进行json和对象的间的转换。
步骤5中ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术,如JSP、freemarker等;步骤6会根据传进来的Model模型数据进行渲染。
在方法的执行前后,还会调用拦截器的相关方法:
拦截器接口:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public interface HandlerInterceptor {
boolean preHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception;
}
preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如我们上一章的Controller实现);返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null;
afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
四. struts2 vs springMVC
- struts2是一个请求一个action,springmvc是单实例的;
- struts2是基于类的横切,springmvc是基于方法的,粒度更细;
- 一个入口是filter,一个入口是servlet,两者各方面实现机制不一样;
- springMVC开发速度和性能由于struts2(不过springMVC性能比struts1差);
- spring体系更加强大和活跃;
- 注解功能强大,支持restful等;
- struts2更新慢,且经常曝出漏洞,风险大;