文章目录
  1. 1. 一.spring版本区别
  2. 2. 二.springMVC执行流程
  3. 3. 三. 源码分析
  4. 4. 四. struts2 vs springMVC
  5. 5. 五. 参考文献

springMVC是spring序列的一个web框架,现在已支持restful架构的风格,有取代struts2的趋势。本文只是将网上的一些文章进行总结归纳引用,便于回忆查找。

一.spring版本区别

  1. Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类。已经@Deprecated。
  2. Spring2.5引入注解式处理器支持,通过@Controller@RequestMapping注解定义我们的处理器类。
    并且提供了一组强大的注解:
    • @Controller:用于标识是处理器类;
    • @RequestMapping:请求到处理器功能方法的映射规则;
    • @RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;
    • @ModelAttribute:请求参数到命令对象的绑定;
    • @SessionAttributes:用于声明session级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session中;
    • @InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;
      注意:需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller 和@RequestMapping注解的处理器。
  3. Spring3.0引入RESTful架构风格支持(通过@PathVariable注解和一些其他特性支持),且又引入了更多的注解支持:
    • @CookieValue:cookie数据到处理器功能处理方法的方法参数上的绑定;
    • @RequestHeader:请求头(header)数据到处理器功能处理方法的方法参数上的绑定;
    • @RequestBody:请求的body体的绑定(通过HttpMessageConverter进行类型转换);
    • @ResponseBody:处理器功能处理方法的返回值作为响应体(通过HttpMessageConverter进行类型转换);
    • @ResponseStatus:定义处理器功能处理方法/异常处理器返回的状态码和原因;
    • @ExceptionHandler:注解式声明异常处理器;
    • @PathVariable:请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;
  4. 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执行流程

图片来自跟开涛学springmvc

核心架构的具体流程步骤如下:

  1. 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
  2. DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
  3. DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
  4. HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
  5. ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
  6. View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
  7. 返回控制权给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
85
protected 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
16
public 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框架图

  1. struts2是一个请求一个action,springmvc是单实例的;
  2. struts2是基于类的横切,springmvc是基于方法的,粒度更细;
  3. 一个入口是filter,一个入口是servlet,两者各方面实现机制不一样;
  4. springMVC开发速度和性能由于struts2(不过springMVC性能比struts1差);
  5. spring体系更加强大和活跃;
  6. 注解功能强大,支持restful等;
  7. struts2更新慢,且经常曝出漏洞,风险大;

五. 参考文献

  1. 拦截器介绍
  2. SpringMVC关于json、xml自动转换的原理研究
  3. springMVC doc
  4. 使用 Spring 3 MVC HttpMessageConverter 功能构建 RESTful web 服务
  5. SpringMVC与Struts2的对比
  6. 跟开涛学SpringMVC
文章目录
  1. 1. 一.spring版本区别
  2. 2. 二.springMVC执行流程
  3. 3. 三. 源码分析
  4. 4. 四. struts2 vs springMVC
  5. 5. 五. 参考文献