一文带你搞懂DispatcherServlet前端控制器

DispatcherServlet前端控制器

配置

DispatcherServlet充当SpringMVC的前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml文件中进行配置

web.xml配置

<!-- springmvc会过滤掉.html的 导致视图解析器无法找到
     如果只是使用jsp资源而未使用html的话可以不配置该项
-->

<servlet-mapping>
  <servlet-name>default</servlet-name>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 配置DispatcherServlet -->
<servlet>
  <servlet-name>DispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- 初始化参数:配置Springmvc配置文件的位置和名称
        默认配置文件为:/WEB-INF/<servlet-name>-servlet.xml
  -->

  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
  </init-param>
  <!-- 指定servlet加载顺序,指定的话,tomcat在容器启动的时候就会初始化并加载servlet实例,值越小,越早加载;如果不指定该值,表示调用servlet请求时在初始化并加载servlet实例 -->
  <!-- 在程序启动的时候根据contextConfigLocation配置的xml文件开始配置spring应用上下文,初始化组件 -->
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>DispatcherServlet</servlet-name>
  <!-- url-pattern匹配:
 精确匹配:/a
 目录匹配:/*
扩展名匹配:*.do
默认匹配:/
这里要配置为/,使之所有在web.xml中找不到匹配元素的url,都会交给DispatcherServlet来处理
-->

  <url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 处理静态资源 -->
<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"></mvc:resources>
<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"></mvc:resources>

serlvet3.0扩展

在servlet3.0中,可以不使用xml配置servlet,容器会在类路径中查找实现ServletContainerInitializer接口的类,会使用它来配置Servlet容器,在spring中提供了该接口的实现SpringServletContainerInitializer,该实现类会去查找实现WebApplicationInitializer接口的类来完成配置任务,AbstractAnnotationConfigDispatcherServletInitializer就是该接口的基础实现,可以通过继承AbstractAnnotationConfigDispatcherServletInitializer类来进行配置DispatcherServlet

WebApplicationInitializer的作用就类似于web.xml,项目启动时会自动执行onStartup方法

需要实现三个方法

// 将一个或多个路径映射到DispacherServlet
protected abstract String[] getServletMappings();

// 返回带有@Configuration注解的类用来定义ContextLoaderLister创建应用上下文中的bean
 protected abstract Class<?>[] getRootConfigClasses();

 // 返回带有@Configuration注解的类用来定义DispacherServlet应用上下文中的bean
 protected abstract Class<?>[] getServletConfigClasses();

静态资源请求问题

因为DispatcherServlet的<url-pattern>配置的是/,针对的是所有请求,对于一些的静态资源(如.js、.css)等也会经过DispatcherServlet,但是DispatcherServlet是处理动态请求的,无法处理静态资源

可以配置<mvc:default-servlet-handler/>来解决,作用是处理静态资源,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandle,对进行DispatcherServlet的请求进行筛选,如果发现是没有经过映射的请求,就将请求交给WEB服务器默认的Servlet来处理,否则交由DispatcherServlet来处理

default-servlet-name默认是default,如果不是default需要显式的进行配置(看所使用的web服务器,tomcat是default)

<mvc:default-servlet-handler default-servlet-name="default"/>

需要注意的是,配置了<mvc:default-servlet-handler/>之后,@RequestMapping的映射会失效,需要加上<mvc:annotation-driven/>配置

源码分析

先看一下DispatcherServlet结构,其本质就是一个Servlet

一文带你搞懂DispatcherServlet前端控制器
DispatcherServlet结构

DispatcherServlet的工作大致可以分为两个部分:一是初始化部分,本质就是一个Servlet,在init方法时会进行初始化,最终调用initStrategies方法;二是对于HTTP请求进行响应,调用doGet和doPost方法,最终调用doDispatch方法

一文带你搞懂DispatcherServlet前端控制器
DispatcherServlet处理过程

初始化

// DispatcherServlet继承了FrameworkServlet类,FrameworkServlet继承了HttpServletBean类,HttpServletBean继承了HttpServlet,所以DispatcherServlet本质上是一个Servlet
public class DispatcherServlet extends FrameworkServlet

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
  
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapableEnvironmentAware

HttpServletBean的init方法

public final void init() throws ServletException {

   // Set bean properties from init parameters.
  // 获取初始化参数 init-param
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
        // 将当前的servlet类转换为一个BeanWrapper,使得可以用spring的方式来对init-param参数进行注入
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
        // 注册自定义属性编辑器
         bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoadergetEnvironment()));
        // 空实现
         initBeanWrapper(bw);
        // 属性注入
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
         
         throw ex;
      }
   }

   // Let subclasses do whatever initialization they like.
  // 调用子类的initServletBean,来进行servletBean的初始化
  // org.springframework.web.servlet.FrameworkServlet#initServletBean
   initServletBean();
}

FrameworkServlet的initServletBean方法

protected final void initServletBean() throws ServletException {   
     // 初始化上下文
      this.webApplicationContext = initWebApplicationContext();
     // 空实现
      initFrameworkServlet();
}

// 创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化
protected WebApplicationContext initWebApplicationContext() {
  // 这是父容器,获取根上下文,ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,是在ContextLoaderListener初始化过程中建立的
  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);
     }
          // 刷新上下文环境
     configureAndRefreshWebApplicationContext(cwac);
    }
   }
  }
  if (wac == null) {
   // No context instance was injected at construction time -> see if one
   // has been registered in the servlet context. If one exists, it is assumed
   // that the parent context (if any) has already been set and that the
   // user has performed any initialization such as setting the context id
      // 根据contextAttribute属性从ServletContext中加载WebApplicationContext
   wac = findWebApplicationContext();
  }
  // 通过上述两种方式都没有找到,则说明不存在WebApplicationContext实例,则进行创建
  if (wac == null) {
   // No context instance is defined for this servlet -> create a local one
   wac = createWebApplicationContext(rootContext);
  }

  if (!this.refreshEventReceived) {
   // Either the context is not a ConfigurableApplicationContext with refresh
   // support or the context injected at construction time had already been
   // refreshed -> trigger initial onRefresh manually here.
      // 调用DispatcherServlet#initStrategies方法,初始化DispatcherServlet的默认策略配置
   onRefresh(wac);
  }
  // 把当前创建的上下文存到ServletContext中
  if (this.publishContext) {
   // Publish the context as a servlet context attribute.
   String attrName = getServletContextAttributeName();
   getServletContext().setAttribute(attrName, wac);
   
  }

  return wac;
 }

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
  Class<?> contextClass = getContextClass();
  
  if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
   throw new ApplicationContextException(
     "Fatal initialization error in servlet with name '" + getServletName() +
     "': custom WebApplicationContext class [" + contextClass.getName() +
     "] is not of type ConfigurableWebApplicationContext");
  }
  // contextClass使用的是默认的DEFAULT_CONTEXT_CLASS,XmlWebApplicationContext.class
  // 通过反射实例化contextClass
  ConfigurableWebApplicationContext wac =
    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

  wac.setEnvironment(getEnvironment());
  // 配置双亲上下文为传入的根上下文,parent是在ConetextLoderListener中创建的实例
  wac.setParent(parent);
  wac.setConfigLocation(getContextConfigLocation());
// 初始化spring环境
  configureAndRefreshWebApplicationContext(wac);

  return wac;
 }
配置刷新WebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
   if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      // The application context id is still set to its original default value
      // -> assign a more useful id based on available information
      if (this.contextId != null) {
         wac.setId(this.contextId);
      }
      else {
         // Generate default id...
         wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
               ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
      }
   }

   wac.setServletContext(getServletContext());
   wac.setServletConfig(getServletConfig());
   wac.setNamespace(getNamespace());
  // 监听器  该监听器在接收到ContextRefreshedEvent事件后,会调用onRefresh方法完成刷新
   wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

   // The wac environment's #initPropertySources will be called in any case when the context
   // is refreshed; do it eagerly here to ensure servlet property sources are in place for
   // use in any post-processing or initialization that occurs below prior to #refresh
   ConfigurableEnvironment env = wac.getEnvironment();
   if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
   }

   postProcessWebApplicationContext(wac);
   applyInitializers(wac);
  // 加载配置文件,这里就是执行org.springframework.context.support.AbstractApplicationContext#refresh方法,与spring中加载上下文是一样的
   wac.refresh();
}
onRefresh

用于刷新spring在web功能实现中所必须使用的全局变量

protected void onRefresh(ApplicationContext context) {
  initStrategies(context);
 }

// 下边的组件如果没有配置默认会读取org.springframework.web.servlet下的DispatcherServlet.properties
protected void initStrategies(ApplicationContext context) {
  // 初始化MultipartResolver,获取beanName为multipartResolver的bean,没有默认值,必须手动配置,用于处理上传请求,处理方法是将普通的request包装成MultipartHttpServletRequest,可以直接调用getFile方法获取File
   initMultipartResolver(context);
  // 初始化LocaleResolver,获取beanName为localeResolver的bean,如果没有默认DispatcherServlet.properties获取
  //国际化解析器,使用的地方有两个,一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
   initLocaleResolver(context);
  // 初始化ThemeResolver,获取beanName为themeResolver的bean,如果没有默认DispatcherServlet.properties获取
  //用于解析主题。SpringMVC中一个主题对应 一个properties文件,里面存放着跟当前主题相关的所有资源,如图片、css样式等。SpringMVC的主题也支持国际化
   initThemeResolver(context);
  // 初始化HandlerMappings,如果配置detectAllHandlerMappings为false的话,只会获取beanName为handlerMapping的bean,否则会找所有类型为HandlerMapping的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  //初始化处理器映射器
   initHandlerMappings(context);
  // 初始化HandlerAdapters,如果配置detectAllHandlerAdapters为false的话,只会获取beanName为handlerAdapter的bean,否则会找所有类型为HandlerAdapter的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  //初始化处理器适配器
   initHandlerAdapters(context);
  // 初始化HandlerExceptionResolvers,如果配置detectAllHandlerExceptionResolvers为false的话,只会获取beanName为handlerExceptionResolver的bean,否则会找所有类型为HandlerExceptionResolver的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  // 初始化异常处理器,对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。
   initHandlerExceptionResolvers(context);
  // 初始化RequestToViewNameTranslator,获取beanName为viewNameTranslator的bean,如果没有默认DispatcherServlet.properties获取
  //有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要RequestToViewNameTranslator从request获取ViewName了
   initRequestToViewNameTranslator(context);
  // 初始化ViewResolvers,如果配置detectAllViewResolvers为false的话,只会获取beanName为viewResolver的bean,否则会找所有类型为ViewResolver的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  //ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
   initViewResolvers(context);
  // 初始化FlashMapManager,获取beanName为flashMapManager的bean,如果没有默认DispatcherServlet.properties获取
  //用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。
   initFlashMapManager(context);
}

上述默认值是从DispatcherServlet.properties中取的

# 国际化解析器
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
# 主题解析器
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
# HandlerMapping
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
# FlashMap管理器
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
介绍各个组件

LocaleResolver

国际化处理

  • AcceptHeaderLocaleResolver  基于URL参数的配置,可以读取url中locale=zh_CN来控制国际化参数
  • CookieLocaleResolver  基于cookie的配置,可以通过浏览器的cookie设置获取Locale对象
  • SessionLocaleResolver  基于session的配置,可以公国检验session的预置的属性来解析,如果没有则会根据请求头中的accept-language来确定

ThemeResolver

主题处理,根据主题控制网页风格

  • FixedThemeResolver  选择一个固定的主题
  • CookieThemeResolver  用于实现用户所选的主题,以cookie的形式存放在客户端的机器上
  • SessionThemeResolver  用于主题存放在session中

HandlerAdapter

  • HttpRequestHandlerAdapter   仅支持HTTP请求处理器的适配,将HTTP请求对象和响应对象传递给HTTP请求处理器的实现,不需要返回值,主要应用在基于HTTP的远程调用实现上
  • SimpleControllerHandlerAdapter  将HTTP请求适配到一个控制器的实现进行处理
  • AnnotationMethodHandlerAdapter   在3.2被废弃,使用RequestMappingHandlerAdapter,基于注解的实现

HandlerExceptionResolver

进行异常处理

处理请求

请求过来之后进行统一处理,由service()/doGet()/doPost()等方法调用

// doPost -> processRequest -> doService -> doDispatch
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException 
{

  processRequest(request, response);
 }

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException 
{

  long startTime = System.currentTimeMillis();
  Throwable failureCause = null;
 // 提取当前线程的LocaleContext和RequestAttributes,在请求结束后进行重置
  LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
  LocaleContext localeContext = buildLocaleContext(request);

  RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
  ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

  WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 将当前请求的LocaleContext和RequestAttributes绑定到当前线程,LocaleContextHolder和RequestContextHolder中
  initContextHolders(request, localeContext, requestAttributes);

  try {
      // 实际处理请求
   doService(request, response);
  }
  catch (ServletException ex) {
   failureCause = ex;
   throw ex;
  }
  catch (IOException ex) {
   failureCause = ex;
   throw ex;
  }
  catch (Throwable ex) {
   failureCause = ex;
   throw new NestedServletException("Request processing failed", ex);
  }

  finally {
      // 请求结束后恢复原始状态
   resetContextHolders(request, previousLocaleContext, previousAttributes);
   if (requestAttributes != null) {
    requestAttributes.requestCompleted();
   }
   // 发布消息
   publishRequestHandledEvent(request, response, startTime, failureCause);
  }
 }


protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

  // 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;
  // include请求,对request中的Attribute进行备份
  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));
    }
   }
  }

  // Make framework objects available to handlers and view objects.
  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.
        // 还原request中的Attribute快照
    if (attributesSnapshot != null) {
     restoreAttributesAfterInclude(request, attributesSnapshot);
    }
   }
  }
 }

// 真正的请求处理
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类型的,如果是会将请求类型转为MultipartHttpServletRequest
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
        // 获取处理当前请求的处理器,根据请求的URL信息去查找匹配的URL的Handler,如果查找成,并返回一个执行链HandlerExecutionChain
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
           // 没有找到对应的handler,返回404
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
        // 根据当前的handler获取对应的HandlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
        // 处理last-modified请求头
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
     // 执行拦截器的preHandler方法,按顺序执行,如果执行过程中有拦截器的preHandler方法返回false,则需要执行拦截器的afterCompletion方法,按照反向顺序进行执行 for (int i = this.interceptorIndex; i >= 0; i--)
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.
        // 调用handler处理器逻辑,即业务代码Controller
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    // 异步处理直接返回
         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
    // 如果没有返回view的视图名称,则采用默认的视图名称,添加前缀、后缀
         applyDefaultViewName(processedRequest, mv);
        // 执行拦截器的postHandle方法,按照反向顺序进行执行 for (int i = interceptors.length - 1; i >= 0; i--)
         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);
      }
     // 处理返回结果,包括视图渲染,处理异常,以及拦截器的afterCompletion方法
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
     // 异常处理,以及拦截器的afterCompletion方法
      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.
        // 清除multipart上传的资源
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}
一文带你搞懂DispatcherServlet前端控制器
doDispatch处理
getHandler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   for (HandlerMapping hm : this.handlerMappings) {
      
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
   return null;
}

这里就以RequestMappingHandlerMapping为例

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  // 获取request对应的handler
   Object handler = getHandlerInternal(request);
   if (handler == null) {
     // 如果没有,则使用默认的handler
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   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;
}
ha.handle

通过适配器去调用Handler的实际逻辑,以RequestMappingHandlerAdapter为例

protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod)
 throws Exception 
{

   ModelAndView mav;
   checkRequest(request);

   // Execute invokeHandlerMethod in synchronized block if required.
  // 如果需要session内的同步执行
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
     // 调用用户逻辑
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }

   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}
invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod)
 throws Exception 
{

   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
     // 获取数据绑定 initBinder
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
     // 获取@SessionAttributes
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      invocableMethod.setDataBinderFactory(binderFactory);
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);

      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.setTaskExecutor(this.taskExecutor);
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

      if (asyncManager.hasConcurrentResult()) {
         Object result = asyncManager.getConcurrentResult();
         mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
         asyncManager.clearConcurrentResult();
         
         invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }
   // 进行方法调用
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }

      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}
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) {
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        // 调用异常处理器处理异常
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // Did the handler return a view to render?
  // 渲染页面
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }
  // 触发Interceptor.afterCompletion
   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}
异常解析processHandlerException
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
      Object handler, Exception ex)
 throws Exception 
{

   // Check registered HandlerExceptionResolvers...
   ModelAndView exMv = null;
  // 使用异常处理器来进行处理
   for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
      exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
      if (exMv != null) {
         break;
      }
   }
   if (exMv != null) {
      if (exMv.isEmpty()) {
         request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
         return null;
      }
      // We might still need view name translation for a plain error model...
      if (!exMv.hasView()) {
         exMv.setViewName(getDefaultViewName(request));
      }
      if (logger.isDebugEnabled()) {
         logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
      }
      WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
      return exMv;
   }

   throw ex;
}

https://zhhll.icu/2021/框架/springmvc/底层剖析/1.DispatcherServlet/


原文始发于微信公众号(bug生产基地):一文带你搞懂DispatcherServlet前端控制器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/291384.html

(0)
追风少年潇歌的头像追风少年潇歌

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!