SpringMVC 框架使用及源码分析

SpringMVC 框架搭建

1. pom文件添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>
    <groupId>com.ming</groupId>
    <artifactId>spring4-mvc-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.24.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.11.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.3</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>spring4-mvc-demo</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. web.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="true">

    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 全局配置参数 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>

    <!-- 配置filter, 将post请求转换为put或delete请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置DispatcherServlet,前置控制器 -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 静态资源的显示 begin -->
    <servlet-mapping >
        <servlet-name >default </servlet-name >
        <url-pattern >*.png</url-pattern>
    </servlet-mapping>
    <servlet-mapping >
        <servlet-name >default </servlet-name >
        <url-pattern >*.jpg</url-pattern>
    </servlet-mapping>
    <servlet-mapping >
        <servlet-name >default </servlet-name >
        <url-pattern >*.gif</url-pattern>
    </servlet-mapping>
    <!-- 静态资源的显示 end -->
</web-app>

3. applicationContext.xml 配置

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context-4.3.xsd "
       default-lazy-init="true">

    <!-- 扫描路径 -->
    <context:component-scan base-package="com.ming.spring4"/>
</beans>

4. spring-servlet.xml 配置

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"
       default-lazy-init="true">

    <!-- 扫描路径 -->
    <context:component-scan base-package="com.ming.spring4.*.controller"/>
    <mvc:annotation-driven />

    <!-- 视图解析器类 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 加载主题资源文件 -->
    <bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
        <!-- 指定文件前缀,即文件所在目录,如果放在classpath下,value为空 -->
        <property name="basenamePrefix" value="theme."/>
    </bean>

    <!-- 使用FixedThemeResolver -->
    <bean id="themeResolver" class="org.springframework.web.servlet.theme.FixedThemeResolver">
        <!-- 定义theme文件名字 -->
        <property name="defaultThemeName" value="myTheme"/>
    </bean>
</beans>

5. 项目层级架构

在这里插入图片描述

SpringMVC 初始化

在这里插入图片描述
以上是 Web 容器的初始化流程。在 web.xml 配置文件中,有两个重要的类:ContextLoaderListenerDispatchServlet。其中,ContextLoaderListener 主要是加载 Dao、Service、DataSource 等组件;DispatcherServlet 主要是加载 Controller 等前台相关的组件。DispatcherServlet 类和ContextLoaderListener 类的关系图:
在这里插入图片描述
用 ContextLoaderListener 初始化各组件的上下文,接着使用 DispatcherServlet 来初始化 WebMVC 的上下文。

1. ContextLoaderListener

在这里插入图片描述
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。当 Servlet 容器启动或终止 Web 应用时,会触发 ServletContextEvent 事件,该事件会由 ServletContextListener 来处理。由上图也可知 ServletContextListener 接口处理 ServletContextEvent 事件主要有两个方法 contextInitialized()contextDestroyed()

ContextLoaderListener 监听器的作用是启动 Web 容器时,自动装配 ApplicationContext 的配置信息。因为它实现了 ServletContextListener 接口,在 web.xml 配置了这个监听器时,就会默认执行它实现的方法。由于在 ContextLoaderListener 中继承了 ContextLoader 这个类,所以整个加载配置过程也是由 ContextLoader 来完成的。

以下是 ContextLoader 的源码分析过程:
在这里插入图片描述
其中上图提到的 **XmlWebApplicationContext **其实就是读取 /WEB-INF/applicationContext.xml。而接下来我们来看下 ContextLoader.configureAndRefreshWebApplicationContext() 方法中的源码实现:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
   if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
      if (idParam != null) {
         wac.setId(idParam);
      }
      else {
         // Generate default id...
         wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
               ObjectUtils.getDisplayString(sc.getContextPath()));
      }
   }

   wac.setServletContext(sc);
    // 获取 “contextConfigLocation” 参数,并设置到WebApplicationContext 参数中,即上面配置文件“applicationContext.xml”
   String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
   if (configLocationParam != null) {
      wac.setConfigLocation(configLocationParam);
   }

   ConfigurableEnvironment env = wac.getEnvironment();
   if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
   }

   customizeContext(sc, wac);
  // 调用AbstractApplicationContext.refresh() 方法
   wac.refresh();
}

AbstractApplicationContext.refresh 方法的源码很重要

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 刷新前的准备工作
      prepareRefresh();

      // 获取一个beanFactory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 对beanFactory进行初始参数设置
      prepareBeanFactory(beanFactory);

      try {
         // 允许在上下文子类中对 beanFactory 进行后置处理
         postProcessBeanFactory(beanFactory);

         // 调用在上下文中注册为beanFactory的处理器
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册拦截 bean 创建的Bean处理器
         registerBeanPostProcessors(beanFactory);

         // 为context初始化消息源
         initMessageSource();

         // 初始化事件发布器
         initApplicationEventMulticaster();

         // 初始化其他特定上下文子类中的其他特殊bean
         onRefresh();

         // 注册事件监听器
         registerListeners();

         // 实例化剩余的(非惰性初始化)的单例bean
         finishBeanFactoryInitialization(beanFactory);

         // 最后一步: 发布相应的事件
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

2. DispatcherServlet

在这里插入图片描述
在 SpringMVC 架构中,DispatcherServlet 负责请求的分发,起到控制器的作用。下面对此类进行详细的源码分析:
在这里插入图片描述

  • 由上面的类依赖关系图可知,DispatcherServlet 其实就是一个 Servlet 的子类,所以 DispatcherServlet 的调用就是 Servlet 类的调用过程。
  • Servlet 接口有两个核心的方法:init()service() 方法,其中 HttpServletBean 重写了 init() 方法。在这部分我们看出其实现的思路:公共的部分统一实现,变化的部分统一来抽象,具体由子类实现。
// HttpServletBean
@Override
public final void init() throws ServletException {
   // Set bean properties from init parameters.
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
         initBeanWrapper(bw);
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
         if (logger.isErrorEnabled()) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
         }
         throw ex;
      }
   }

   // 具体在子类FrameworkServlet中进行了重写
   initServletBean();
}
  • FrameworkServlet 提供了整合 web javabean和 Spring application Context 的方案。在源码中可以看到通过执行 initWebApplicationContext() 方法和initFrameworkServlet() 方法实现。而 initWebApplicationContext() 方法调用了一个空方法 onRefresh() 方法,此方法在子类 DispatcherServlet 进行了重写。
protected final void initServletBean() throws ServletException {
   getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
   if (this.logger.isInfoEnabled()) {
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
   }
   long startTime = System.currentTimeMillis();

   try {
      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet(); //实现为空
   }
   catch (ServletException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
   }
   catch (RuntimeException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
   }

   if (this.logger.isInfoEnabled()) {
      long elapsedTime = System.currentTimeMillis() - startTime;
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
            elapsedTime + " ms");
   }
}

protected WebApplicationContext initWebApplicationContext() {
   // 获取父ApplicationContext, 即ContextLoaderListener加载后的ApplicationContext
   WebApplicationContext rootContext =
         WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;

   if (this.webApplicationContext != null) {
      // A context instance was injected at construction time -> use it
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
         if (!cwac.isActive()) {
            // The context has not yet been refreshed -> provide services such as
            // setting the parent context, setting the application context id, etc
            if (cwac.getParent() == null) {
               // The context instance was injected without an explicit parent -> set
               // the root application context (if any; may be null) as the parent
               cwac.setParent(rootContext);
            }
            // 如果webApplicationContext已经存在,则需要进行配置即刷新
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
   if (wac == null) {
      // 通过contextAttribute参数获取ApplicationContext
      wac = findWebApplicationContext();
   }
   if (wac == null) {
      // 仍然没有context,则根据父上下文创建一个本地Context
      wac = createWebApplicationContext(rootContext);
   }

    // 判断context是否被刷新过,没有则调用DispatcherServlet的onRefresh方法进行刷新
   if (!this.refreshEventReceived) {
      onRefresh(wac);
   }

   if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
      if (this.logger.isDebugEnabled()) {
         this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
               "' as ServletContext attribute with name [" + attrName + "]");
      }
   }

   return wac;
}
  • DispatcherServlet 是 HTTP 请求的中央调度处理器,它将 Web 请求转发 Controller 层处理,提供了敏捷的映射和异常处理机制。其中初始化方法源码如下:
@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}


/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
   // 获取配置文件 multipartResolver 的相关配置,用于文件上传
   initMultipartResolver(context);
   // 获取配置文件 localeResolver 的相关配置,用于国际化解析器的初始化
   initLocaleResolver(context);
   // 获取配置文件 themeResolver 的相关配置,用于主题解析器的初始化
   initThemeResolver(context);
   // 获取配置文件 handlerMapping 的相关配置,用于处理器映射器的初始化
   initHandlerMappings(context);
   // 获取配置文件 handlerAdapter的相关配置,用于处理器适配器的初始化
   initHandlerAdapters(context);
   // 获取配置文件 handlerExceptionResolver 的相关配置,用于异常处理解析器的初始化
   initHandlerExceptionResolvers(context);
   // 获取配置文件 viewNameTranslator 的相关配置, 用于viewNameTranslator的初始化
   initRequestToViewNameTranslator(context);
   // 获取配置文件 viewResolver 的相关配置, 用于视图解析器的初始化
   initViewResolvers(context);
   // 获取配置文件 flashMapManager 的相关配置, 用于视图解析器的初始化
   initFlashMapManager(context);
}

DispatcherServlet 类中重要属性:
1、HandlerMapping:用于 handlers 映射请求和一系列的对于拦截器的前置处理和后置处理,大部分用 @Controller 注解。
2、HandlerAdapter:帮助DispatcherServlet 处理映射请求处理程序的适配器。
3、ViewResolver:根据实际配置解析实际的 View 类型。
4、ThemeResolver:解决Web应用程序使用的主题,丽日提供个性化布局。
5、MultipartResolver:解析多部分请求,支持从 HTML 表单上传文件。
6、FlashMapManager: 存储并检索用于将一个请求属性传递到另一个请求的 input 和output 的FlashMap,通常用于重定向。

其中 DispatcherServlet 针对以上参数设置默认配置,配置文件在 DispatcherServler.properties 文件有体现:

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

SpringMVC 框架执行流程

在这里插入图片描述
SpringMVC执行流程:
* 用户发送请求至前端控制器DispatcherServlet。
* DispatcherServlet收到请求调用处理器映射器HandlerMapping。
* 处理映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain。
* DispatcherServlet根据处理器获取请求的处理器适配器HandlerAdapter。并且执行后续一系列的操作,如:参数封装,数据格式转换,数据验证等操作。
* 执行Handler处理器(比如Controller,页面控制器)
* Handler执行完毕返回ModelAndView。
* HandlerAdapter将ModelAndView再返回给DispatcherServlet。
* DispatcherServlet将ModelAndView传给ViewResolver视图解析器。
* ViewResolver解析后返回具体的物理View
* DispatcherServlet对View进行渲染视图
* DispatcherServlet再响应给用户

1. 主流程

DispatcherServler 的源码如下:

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   if (logger.isDebugEnabled()) {
      String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
      logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
            " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
   }

   // Keep a snapshot of the request attributes in case of an include,
   // to be able to restore the original attributes after the include.
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<String, Object>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }

   // 对request相关属性进行设值
   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
   request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
   request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
   request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

   FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
   if (inputFlashMap != null) {
      request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
   }
   request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
   request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

   try {
      // 请求的分发处理
      doDispatch(request, response);
   }
   finally {
      if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
   }
}

DispatcherServlet 的请求入口方法为 doService(), 在此方法中对请求参数进行默认值的设置。而具体的请求分发交由 doDispatch() 方法实现。

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 {
        // 校验本次请求是否为文件上传, 如果是通过相关解析器进行解析(比如:CommonsMultipartResolver)
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // 由当前请求解析获取具体Handler,并进行包装返回HandlerExecutionChain
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
         // 根据上面获取的Handler,获取处理适配器HandlerAdapter
         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()) {
               logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
            }
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

        // 执行已注册拦截器的preHandle方法(解析Controller前触发)
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 真正调用handle具体逻辑,并返回ModelAndView
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

        // 对于ModelAndView没有视图名,设置为默认的视图名
         applyDefaultViewName(processedRequest, mv);

        // 执行已注册拦截器的postHandle方法(解析完Controller后,封装渲染View之前触发)
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      // 处理请求分发的结果
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

2. 子流程 getHandler() 方法

// DispatcherServlet 
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 循环判断HandlerMapping解析出handler
   for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
         logger.trace(
               "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      // 根据不同的handlerMapping解析请求,并组装成执行链
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
   return null;
}

而 getHandler() 方法又调用了 HandlerMapping 接口的抽象类 AbstractHandlerMapping.getHandler() 方法。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 根据请求对象获取handler处理器
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // 根据bean的name获取对象bean
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = getApplicationContext().getBean(handlerName);
   }
    
    // 获取处理器执行链
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   if (CorsUtils.isCorsRequest(request)) {
      CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
   return executionChain;
}

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   // 将handler转换为HandlerExecutionChain
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
   // 根据请求路径,判断被哪个拦截器进行了处理,并设置该拦截器
   String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
   for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
      if (interceptor instanceof MappedInterceptor) {
         MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
         if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
         }
      }
      else {
         chain.addInterceptor(interceptor);
      }
   }
   return chain;
}

3. 子流程 getHandlerAdapter() 方法

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   // 循环访问handlerAdapter的实现类
   for (HandlerAdapter ha : this.handlerAdapters) {
      if (logger.isTraceEnabled()) {
         logger.trace("Testing handler adapter [" + ha + "]");
      }
      // 判断handler是哪个HandlerAdapter适配解析的,并返回该HandlerAdapter
      if (ha.supports(handler)) {
         return ha;
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

4. 子流程 processDispatchResult() 方法

对于分发请求处理结果的处理,源码如下:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
   boolean errorView = false;
   // 判断是否有异常
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         // 其他通过异常处理类来解析
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // 对ModelAndView进行渲染
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isDebugEnabled()) {
         logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
               "': assuming HandlerAdapter completed request handling");
      }
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }

   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}


protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   // 根据请求对象设置返回对象的国际化
   Locale locale = this.localeResolver.resolveLocale(request);
   response.setLocale(locale);

   View view;
   // 判断ModelAndView是否为String
   if (mv.isReference()) {
      // 根据viewName遍历配置的ViewResolver解析出View对象
      view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
      if (view == null) {
         throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
               "' in servlet with name '" + getServletName() + "'");
      }
   }
   else {
      // 如果mv是ModelAndView对象,可以直接获取View对象
      view = mv.getView();
      if (view == null) {
         throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
               "View object in servlet with name '" + getServletName() + "'");
      }
   }

   // Delegate to the View object for rendering.
   if (logger.isDebugEnabled()) {
      logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
   }
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
      // 视图对象的渲染
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
               getServletName() + "'", ex);
      }
      throw ex;
   }
}

总结

SpringMVC 框架的核心为 web.xml, 而 web.xml 配置文件需要配置 ContextLoaderListener,此配置项是为了指定 applicationContext.xml 配置文件,用于扫描 bean 的一些配置。此外 web.xml 配置文件还需要配置 DispatcherServlet,此配置项用于处理前台请求,然后通过HandlerMapping 选取具体的 HandlerAdapter 处理具体的 Controller 层,然后返回 ModelAndView 给前台视图解析器进行渲染,最终在前台展示即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值