概述

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<String\> requiredProperties)   throws ServletException {     Set<String\> missingProps \= (!CollectionUtils.isEmpty(requiredProperties) ?   new HashSet<>(requiredProperties) : null);     //获取ServletConfig外面的InitParameter转换为PropertyValue   Enumeration<String\> 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