关于spring-mvc:DispatcherServlet

3次阅读

共计 11458 个字符,预计需要花费 29 分钟才能阅读完成。

概述

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
正文完
 0