核心类与接口

- DispatcherServlet 前置控制器
- HandlerMapping 请求映射(到Controller)
- HandlerAdapter 请求映射(到Controller类的方法上)
- Controller 控制器
- HandlerIntercepter 拦截器
- ViewResolver 视图映射
- View 视图处理

启动过程

Spring MVC启动过程大致分为两个过程:
- ContextLoaderListener初始化,读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context,通过调用继承自ContextLoader的initWebApplicationContext方法实例化Spring IoC容器,并将此容器实例注册到ServletContext中
- ContextLoaderListener初始化完毕后,开始初始化web.xml中配置的DispatcherServlet,DispatcherServlet的初始化又包括了视图管理器、异常处理器、映射管理等等;

  1. /**
  2. * Initialize the strategy objects that this servlet uses.
  3. * <p>May be overridden in subclasses in order to initialize further strategy objects.
  4. */
  5. protected void initStrategies(ApplicationContext context) {
  6. initMultipartResolver(context);
  7. initLocaleResolver(context);
  8. initThemeResolver(context);
  9. initHandlerMappings(context);
  10. initHandlerAdapters(context);
  11. initHandlerExceptionResolvers(context);
  12. initRequestToViewNameTranslator(context);
  13. initViewResolvers(context);
  14. initFlashMapManager(context);
  15. }

ContextLoaderListener初始化的是 WebApplicationContext, 创建后可以从ServletContext中获取,WebApplicationContext是应用程序内共享的,最多只有一个,如果寻求简单也可以不初始化此容器。与之不同 DispatcherServlet可以有多个,并共享一个WebApplicationContext容器,每一个DispatcherServlet有不同的配置,控制不同的WEB访问。一般将 DAO、Service 层Bean共享的放在ContextLoaderListener配置的容器中,将WEB层的Bean放在特定的DispatcherServlet配置的容器中。

SpringMVC利用Spring的注入特性初始化资源文件,只需要调用setPropertyValues方法就可将contextConfigLocation属性设置到对应实例中,也就是以依赖注入的方式初始化属性。

时序图如下(盗图):

请求处理流程

官网上的图

涉及到核心类与接口的过程描述:

客户端浏览器发送http请求,被`DispatcherServlet`捕获,调用关键的doDispatch方法,遍历所有注册为`Controller`的bean,为请求寻找关联映射,其中遍历查找的函数getHandler和getHandlerAdapter的源码:

  1. /**
  2. * Return the HandlerExecutionChain for this request.
  3. * <p>Tries all handler mappings in order.
  4. * @param request current HTTP request
  5. * @return the HandlerExecutionChain, or {@code null} if no handler could be found
  6. */
  7. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  8. for (HandlerMapping hm : this.handlerMappings) {
  9. if (logger.isTraceEnabled()) {
  10. logger.trace(
  11. "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
  12. }
  13. HandlerExecutionChain handler = hm.getHandler(request);
  14. if (handler != null) {
  15. return handler;
  16. }
  17. }
  18. return null;
  19. }
  20.  
  21. /**
  22. * Return the HandlerAdapter for this handler object.
  23. * @param handler the handler object to find an adapter for
  24. * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
  25. */
  26. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  27. for (HandlerAdapter ha : this.handlerAdapters) {
  28. if (logger.isTraceEnabled()) {
  29. logger.trace("Testing handler adapter [" + ha + "]");
  30. }
  31. if (ha.supports(handler)) {
  32. return ha;
  33. }
  34. }
  35. throw new ServletException("No adapter for handler [" + handler +
  36. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  37. }

找到匹配的映射后`HandlerAdapter`会依次调用preHandle、handle(返回ModelAndView)、postHandle方法,所有步骤完成后调用processDispatchResult函数处理结果,并返回View给客户端。postDispatchResult函数和其中调用的render函数源码如下:

  1. /**
  2. * Handle the result of handler selection and handler invocation, which is
  3. * either a ModelAndView or an Exception to be resolved to a ModelAndView.
  4. */
  5. private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
  6. HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
  7.  
  8. boolean errorView = false;
  9.  
  10. if (exception != null) {
  11. if (exception instanceof ModelAndViewDefiningException) {
  12. logger.debug("ModelAndViewDefiningException encountered", exception);
  13. mv = ((ModelAndViewDefiningException) exception).getModelAndView();
  14. }
  15. else {
  16. Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  17. mv = processHandlerException(request, response, handler, exception);
  18. errorView = (mv != null);
  19. }
  20. }
  21.  
  22. // Did the handler return a view to render?
  23. if (mv != null && !mv.wasCleared()) {
  24. render(mv, request, response);
  25. if (errorView) {
  26. WebUtils.clearErrorRequestAttributes(request);
  27. }
  28. }
  29. else {
  30. if (logger.isDebugEnabled()) {
  31. logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
  32. "': assuming HandlerAdapter completed request handling");
  33. }
  34. }
  35.  
  36. if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  37. // Concurrent handling started during a forward
  38. return;
  39. }
  40.  
  41. if (mappedHandler != null) {
  42. mappedHandler.triggerAfterCompletion(request, response, null);
  43. }
  44. }
  45.  
  46. /**
  47. * Render the given ModelAndView.
  48. * <p>This is the last stage in handling a request. It may involve resolving the view by name.
  49. * @param mv the ModelAndView to render
  50. * @param request current HTTP servlet request
  51. * @param response current HTTP servlet response
  52. * @throws ServletException if view is missing or cannot be resolved
  53. * @throws Exception if there's a problem rendering the view
  54. */
  55. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
  56. // Determine locale for request and apply it to the response.
  57. Locale locale = this.localeResolver.resolveLocale(request);
  58. response.setLocale(locale);
  59.  
  60. View view;
  61. if (mv.isReference()) {
  62. // We need to resolve the view name.
  63. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
  64. if (view == null) {
  65. throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
  66. "' in servlet with name '" + getServletName() + "'");
  67. }
  68. }
  69. else {
  70. // No need to lookup: the ModelAndView object contains the actual View object.
  71. view = mv.getView();
  72. if (view == null) {
  73. throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
  74. "View object in servlet with name '" + getServletName() + "'");
  75. }
  76. }
  77.  
  78. // Delegate to the View object for rendering.
  79. if (logger.isDebugEnabled()) {
  80. logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
  81. }
  82. try {
  83. if (mv.getStatus() != null) {
  84. response.setStatus(mv.getStatus().value());
  85. }
  86. view.render(mv.getModelInternal(), request, response);
  87. }
  88. catch (Exception ex) {
  89. if (logger.isDebugEnabled()) {
  90. logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
  91. getServletName() + "'", ex);
  92. }
  93. throw ex;
  94. }
  95. }

这就是一个完整的处理http请求的过程。盗图一张:

时序图如下(来源:http://neoremind.com/2016/02/springmvc%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/):

配置实例

这里放的是最简单的配置,可以通过这个简单的配置实例回顾一下上面的过程。

目录结构

-SpringMVCDemo
        -src
            -me.cyan
                -WelcomeController
        -web
            -WEB-INF
                -applicationContext.xml
                -dispatcher-servlet.xml
                -web.xml
            -index.jsp
        -pom.xml
        
pom.xml
引入的包

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>me.cyan</groupId>
  6. <artifactId>SpringMVCDemo</artifactId>
  7. <name>SpringMVCDemo</name>
  8. <packaging>war</packaging>
  9. <version>1.0.0</version>
  10.  
  11. <properties>
  12. <spring-version>4.2.6.RELEASE</spring-version>
  13. </properties>
  14.  
  15. <dependencies>
  16. <dependency>
  17. <groupId>org.springframework</groupId>
  18. <artifactId>spring-context</artifactId>
  19. <version>${spring-version}</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework</groupId>
  23. <artifactId>spring-core</artifactId>
  24. <version>${spring-version}</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework</groupId>
  28. <artifactId>spring-web</artifactId>
  29. <version>${spring-version}</version>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework</groupId>
  33. <artifactId>spring-beans</artifactId>
  34. <version>${spring-version}</version>
  35. </dependency>
  36. <dependency>
  37. <groupId>commons-logging</groupId>
  38. <artifactId>commons-logging</artifactId>
  39. <version>1.2</version>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework</groupId>
  43. <artifactId>spring-webmvc</artifactId>
  44. <version>${spring-version}</version>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.springframework</groupId>
  48. <artifactId>spring-aop</artifactId>
  49. <version>${spring-version}</version>
  50. </dependency>
  51. <dependency>
  52. <groupId>org.springframework</groupId>
  53. <artifactId>spring-expression</artifactId>
  54. <version>${spring-version}</version>
  55. </dependency>
  56. </dependencies>
  57. </project>

web.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  5. version="3.1">
  6.  
  7. <!--配置文件路径-->
  8. <context-param>
  9. <param-name>contextConfigLocation</param-name>
  10. <param-value>/WEB-INF/applicationContext.xml</param-value>
  11. </context-param>
  12. <listener>
  13. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  14. </listener>
  15.  
  16. <!--SpringMVC核心servlet-->
  17. <servlet>
  18. <servlet-name>dispatcher</servlet-name>
  19. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  20. <load-on-startup>1</load-on-startup>
  21. </servlet>
  22. <servlet-mapping>
  23. <servlet-name>dispatcher</servlet-name>
  24. <url-pattern>/</url-pattern>
  25. </servlet-mapping>
  26. </web-app>

dispatcher-servlet.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  6.  
  7. <!-- 默认的注解映射的支持 -->
  8. <mvc:annotation-driven />
  9.  
  10. <!-- 自动扫描的包名 -->
  11. <context:component-scan base-package="me.cyan" />
  12.  
  13. </beans>

WelcomeController

  1. package me.cyan;
  2.  
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.ResponseBody;
  6.  
  7. /**
  8. * Created by cyan on 16/5/23.
  9. */
  10.  
  11. @Controller
  12. public class welcomeController {
  13.  
  14. @RequestMapping("/hello")
  15. public @ResponseBody String sayhello(){
  16. return "hello Spring MVC!";
  17. }
  18. }

运行结果

SpringMVC深入理解的更多相关文章

  1. 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化

    关于IOC容器的初始化,结合之前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分: 一.IOC的初始化过程,结合代码和debug过程重点说明 1. 为什么要debug? ...

  2. springMVC新理解

    springmvc 中@Controller和@RestController的区别 1. Controller, RestController的共同点 都是用来表示spring某个类的是否可以接收HT ...

  3. 使用mybatis多表联查的时候结果异常及springmvc的理解

    今天使用mybatis多表联查的时候,在dos窗口查询时可以出结果集,但是使用mybatis查询的时候最后返回的结果只有最后一个结果 然后研究了半天没弄出来,后来无意中发现添加了最外层从表的ID字段后 ...

  4. 对Spring与SpringMVC的理解

    Spring 在我的Spring --简介及环境搭建跑通Hello提到关于Spring的基本结构与功能 SpringMVC 先上一张SpringMVC的流程图 Spring MVC 是一个模型 - 视 ...

  5. SSM-SpringMVC-04:SpringMVC深入浅出理解HandleMapping(源码刨析)

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 先从概念理解,从中央调度器,携带参数request,调度到HandleMapping处理器映射器,处理器映射器 ...

  6. SpringMVC的理解

    SpringMVC工作原理 SpringMvc是基于过滤器对servlet进行了封装的一个框架,我们使用的时候就是在web.xml文件中配置DispatcherServlet类:SpringMvc工作 ...

  7. 对Spring 及SpringMVC的理解

    spring是一个轻型容器(light-weight Container),其核心是Bean工厂(Bean Factory),用以构造我们所需要的M(Model).在此基础之上,Spring提供了AO ...

  8. Spring SpringMVC myBatis(简称SSM)理解

    1对Spring的理解 (1)spring是什么? spring是Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架. (2)spring的作用 (a)spring ...

  9. SpringMVC学习

    1,对SpringMVC的理解 a,基于MVC的设计理念 b,采用松散耦合可插拔组件结构,比其他MVC框架更具扩展性和灵活性 c,支持REST风格的URL请求 d,该框架围绕DispatcherSer ...

随机推荐

  1. PHP比较全的友好的时间显示,比如‘刚刚’,'几秒前'等

    分享一个php友好的比较完成的时间格式化函数,包括‘刚刚’,'几秒之前',‘几分钟前’,'几小时前',几天前,几周前,几个月前等.调用方式很简单,是从ThinkSNS 里面拿出来的. /** * 友好 ...

  2. velocity 字符串 转化为数字

    #set($nPageIndex=$request.getParameter("nIndex")) #set($Integer = 0 ) #set($FrontPageSize= ...

  3. [转]easyui常用控件及样式收藏

    CSS类定义: div easyui-window                               window窗口样式 属性如下: 1)       modal:是否生成模态窗口.tru ...

  4. java文本编辑器5

    package peng_jun; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.* ...

  5. MVC自学第三课

    上一课我们已经能够做出简单的HTML显示页面,并懂得了MVC的工作模式,这一课我们讲解一些动态的数据交互. 在MVC中,控制器的工作是构造某些数据,而视图的工作是把它渲染成HTML.数据是从控制器传递 ...

  6. 【Chromium中文文档】多进程资源加载

    多进程资源加载(需要更新) 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture ...

  7. scss + react + webpack + es6

    scss + react + webpack + es6 写在前面: 刚学习完慕课网里的一个幻灯片案例,自己加了刚学的react,两者结合.首先让大家看看效果 点击此处 你可以先用纯js实现上面的效果 ...

  8. J2SE知识点摘记(十九)

    Collection 1.2.1         常用方法 Collection 接口用于表示任何对象或元素组.想要尽可能以常规方式处理一组元素时,就使用这一接口.Collection 在前面的大图也 ...

  9. 运用JavaScript构建你的第一个Metro式应用程序(on Windows 8)(一)

    原文 http://blog.csdn.net/zhangxin09/article/details/6784547 作者:Chris Sells 译: sp42   原文 包括 HTML.CSS 和 ...

  10. sshuttle基于VPN的透明代理,安全连接

    sshuttle基于VPN的透明代理, 通过 ssh 创建一条从你电脑连接到任何远程服务器的 VPN 连 sudo sshuttle -r username@sshserver 0.0.0.0/0 - ...