DispatcherServlet

概述

Servlet WebApplicationContext容器的初始化基于我们配置的DispatcherServlet,DispatcherServlet是Servlet 的实现,当Servlet进行初始化调用DispatcherServlet的父类HttpServletBean的init方法进行Servlet的初始化。复写Servlet的init方法的关键步骤如下:

  1. 将web.xml里面的配置进行赋值
  2. 可以通过4种不同的方式初始化Servlet WebApplicationContext容器,进行配置和刷新
  3. 初始化HandlerMapping,HandlerAdapter等组件

DispatcherServlet类的UML

我们看关键的几个实现之间的关系和功能

  1. HttpServletBean : 将ServletConfig里面的配置赋值到Servlet
  2. FrameworkServlet : 初始化和配置刷新Servlet WebApplicationContext容器
  3. DispatcherServlet :初始化 Spring MVC的各个组件,以及处理请求

整体流程

  1. Servlet WebApplicationContext容器的初始化发生在,DispatcherServlet调用HttpServletBean的init方法,进行Servlet的初始化。主要将ServletConfig里面的配置赋值到Servlet。
  2. 之后调用FrameworkServlet的initServletBean方法进行初始化Servlet WebApplicationContext,并配置和刷新上下文
  3. 最后调用DispatcherServlet的onRefresh方法,进行组件的初始化

分步解释

  • 调用HttpServletBean的init方法,将 ServletConfig 里面的InitParameter赋值到当前Servlet对象中

    • 将ServletConfig里面的InitParameter配置转为PropertyValues,同时校验必须配置的配置项
    • 将PropertyValues里面的配置赋值到Servlet

//org.springframework.web.servlet.HttpServletBean  
public final void init() throws ServletException {  
​  
 // Set bean properties from init parameters.  
 // 将ServletConfig里面的配置转为PropertyValues,同时校验必须配置的配置项  
 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);  
 // 将ServletConfig里面的配置赋值到Servlet  
 bw.setPropertyValues(pvs, true);  
 }  
 catch (BeansException ex) {  
 if (logger.isErrorEnabled()) {  
 logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);  
 }  
 throw ex;  
 }  
 }  
​  
 // Let subclasses do whatever initialization they like.  
 //FrameworkServlet进行了重载  
 initServletBean();  
 }
  • 将ServletConfig里面的InitParameter配置转为PropertyValues,同时校验必须配置的配置项

    • 获取ServletConfig里面的InitParameter转换为PropertyValue
    • 将必填的里面的属性去除从InitParameter存在的属性
    • 校验是否全部必填的配置是否填

public ServletConfigPropertyValues(ServletConfig config, Set requiredProperties)  
 throws ServletException {  
​  
 Set missingProps \= (!CollectionUtils.isEmpty(requiredProperties) ?  
 new HashSet<>(requiredProperties) : null);  
​  
 //获取ServletConfig里面的InitParameter转换为PropertyValue  
 Enumeration paramNames \= config.getInitParameterNames();  
 while (paramNames.hasMoreElements()) {  
 String property \= paramNames.nextElement();  
 Object value \= config.getInitParameter(property);  
 addPropertyValue(new PropertyValue(property, value));  
 //  将必填的里面的属性去除从InitParameter存在的属性  
 if (missingProps != null) {  
 missingProps.remove(property);  
 }  
 }  
​  
 // Fail if we are still missing properties.  
 // 校验是否全部必填的配置是否填  
 if (!CollectionUtils.isEmpty(missingProps)) {  
 throw new ServletException(  
 "Initialization from ServletConfig for servlet '" + config.getServletName() +  
 "' failed; the following required properties were missing: " +  
 StringUtils.collectionToDelimitedString(missingProps, ", "));  
 }  
 }
  • //org.springframework.web.servlet.FrameworkServlet
    protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
    logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime \= System.currentTimeMillis();

    try {
    //初始化上下文
    this.webApplicationContext \= initWebApplicationContext();
    initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
    logger.error("Context initialization failed", ex);
    throw ex;
    }

    if (logger.isDebugEnabled()) {
    String value \= this.enableLoggingRequestDetails ?
    "shown which may lead to unsafe logging of potentially sensitive data" :
    "masked to prevent unsafe logging of potentially sensitive data";
    logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
    "': request parameters and headers will be " + value);
    }

    if (logger.isInfoEnabled()) {
    logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }
    }
  • 调用FrameworkServlet的initWebApplicationContext进行初始化,方式有四种,先看initWebApplicationContext的方法,然后具体看初始化的方法

    • 如果已经实例化Servlet WebApplicationContext,那么将父上下文进行赋值,同时配置和刷新Servlet WebApplicationContext上下文
    • 如果没有实例化Servlet WebApplicationContext,那么调用findWebApplicationContext方法,从Servlet上下文中通过contextAttribute属性获取
    • 如果还是没有实例化Servlet WebApplicationContext,那么调用createWebApplicationContext,通过属性contextClass赋值的常量DEFAULT\_CONTEXT\_CLASS获取类名,然后通过反射进行获取
    • 给子类进行一些刷新工作,DispatcherServlet的初始化组件
    • 将Servlet WebApplicationContext存储在ServletContext

// org.springframework.web.servlet.FrameworkServlet  
protected WebApplicationContext initWebApplicationContext() {  
​  
 // 从ContextLoaderListener初始化Root WebApplicationContext赋值到ServletContext存储的上下文获取作为父上下文  
 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  
 //将RootWebApplicationContext上下文作为父上下文  
 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  
 // 从Servlet上下文中通过contextAttribute属性获取  
 wac \= findWebApplicationContext();  
 }  
 if (wac \== null) {  
 // No context instance is defined for this servlet -> create a local one  
 //通过属性contextClass赋值的常量DEFAULT\_CONTEXT\_CLASS获取类名,然后通过反射进行获取  
 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.  
 synchronized (this.onRefreshMonitor) {  
 //给子类进行一些刷新工作,DispatcherServlet的初始化组件  
 onRefresh(wac);  
 }  
 }  
​  
 //将Servlet WebApplicationContext存储在ServletContext  
 if (this.publishContext) {  
 // Publish the context as a servlet context attribute.  
 String attrName \= getServletContextAttributeName();  
 getServletContext().setAttribute(attrName, wac);  
 }  
​  
 return wac;  
 }
  • 已经赋值的方式,有两种

    • 通过构造器进行初始化

      //org.springframework.web.servlet.FrameworkServlet
      public FrameworkServlet(WebApplicationContext webApplicationContext) {
      this.webApplicationContext \= webApplicationContext;
      }

    • 实现ApplicationContextAware接口,调用setApplicationContext方法进行初始化

      //org.springframework.web.servlet.FrameworkServlet
      public void setApplicationContext(ApplicationContext applicationContext) {
      if (this.webApplicationContext \== null && applicationContext instanceof WebApplicationContext) {
      //进行初始化
      this.webApplicationContext \= (WebApplicationContext) applicationContext;
      this.webApplicationContextInjected \= true;
      }
      }

    • 调用FrameworkServlet的findWebApplicationContext获取上下文

      • 如果web.xml里面的配置进行了配置,那么会通过HttpServletBean里面将属性contextAttribute赋值,可以获取到attrName
      • 从ServletContext上下文中通过属性attrName获取
    
    //org.springframework.web.servlet.FrameworkServlet  
    protected WebApplicationContext findWebApplicationContext() {  
     // 如果web.xml里面的配置进行了配置,那么会通过HttpServletBean里面将属性contextAttribute赋值,可以获取到attrName  
     String attrName \= getContextAttribute();  
     if (attrName \== null) {  
     return null;  
     }  
     // 从ServletContext上下文中通过属性attrName获取  
     WebApplicationContext wac \=  
     WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);  
     if (wac \== null) {  
     throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");  
     }  
     return wac;  
     }
    
*   调用FrameworkServlet的`createWebApplicationContext`进行初始化
    
    *   获取常量DEFAULT\_CONTEXT\_CLASS,也就是XmlWebApplicationContext.class作为上下文的Class类
        
    *   通过反射进行初始化
        
    *   将RootWebApplicationContext作为父容器
        
    *   进行上下文刷新和配置
        
    
    //org.springframework.web.servlet.FrameworkServlet  
    protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {  
     return createWebApplicationContext((ApplicationContext) parent);  
     }
    
    //org.springframework.web.servlet.FrameworkServlet  
    protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {  
     //获取常量DEFAULT\_CONTEXT\_CLASS,也就是XmlWebApplicationContext.class  
     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");  
     }  
     //通过反射进行初始化  
     ConfigurableWebApplicationContext wac \=  
     (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
    ​  
     wac.setEnvironment(getEnvironment());  
     //将RootWebApplicationContext作为父容器  
     wac.setParent(parent);  
     String configLocation \= getContextConfigLocation();  
     if (configLocation != null) {  
     wac.setConfigLocation(configLocation);  
     }  
     //进行上下文刷新  
     configureAndRefreshWebApplicationContext(wac);  
    ​  
     return wac;  
     }
    
*   调用FrameworkServlet的`configureAndRefreshWebApplicationContext` 进行配置和刷新
    
    *   将ServletContext,ServletConfig,Namespace赋值到上下文
        
    *   添加SourceFilteringListener,用于上下文刷新完毕调用AbstractApplicationContext的`finishRefresh`方法,然后调用AbstractApplicationContext的`publishEvent`发布ContextRefreshListener,监听器监听刷新完毕调用ContextRefreshListener的`onApplicationEvent`方法,然后进行调用`FrameworkServlet.this.onApplicationEvent(event)`用于刷新Spring MVC组件的初始化
        
    *   刷新上下文
        
    
    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());  
     }  
     }  
    ​  
     // 将ServletContext,ServletConfig,Namespace赋值到上下文  
     wac.setServletContext(getServletContext());  
     wac.setServletConfig(getServletConfig());  
     wac.setNamespace(getNamespace());  
     // 添加SourceFilteringListener,用于上下文刷新完毕调用AbstractApplicationContext的finishRefresh方法,然后调用AbstractApplicationContext的publishEvent发布ContextRefreshListener,监听器监听刷新完毕调用ContextRefreshListener的\`onApplicationEvent\`方法,然后进行调用FrameworkServlet.this.onApplicationEvent(event)用于刷新Spring MVC组件的初始化  
     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);  
     //刷新上下文  
     wac.refresh();  
     }
    
*   调用DispatcherServlet的`onRefresh`方法进行组件初始化
    
    //org.springframework.web.servlet.DispatcherServlet  
    @Override  
     protected void onRefresh(ApplicationContext context) {  
     initStrategies(context);  
     }
    
*   调用DispatcherServlet的`initStrategies`初始化组件
    
      
    //org.springframework.web.servlet.DispatcherServlet  
    protected void initStrategies(ApplicationContext context) {  
     initMultipartResolver(context);  
     initLocaleResolver(context);  
     initThemeResolver(context);  
     initHandlerMappings(context);  
     initHandlerAdapters(context);  
     initHandlerExceptionResolvers(context);  
     initRequestToViewNameTranslator(context);  
     initViewResolvers(context);  
     initFlashMapManager(context);  
     }
    
  • 调用FrameworkServlet的initServletBean方法进行初始化ServletWebApplicationContext

你可能感兴趣的:(spring-mvc)