SpringMVC源码分析之策略对象初始化

简介: SpringMVC源码分析之策略对象初始化

前面我们分析了springmvc的各种组件,那么组件在什么时候初始化的呢?我们这里就研究一下。

【1】DispatcherServlet

① 核心属性


DispatcherServlet有许多属性,除了一些 static final类型的常量,其他类似如List<HandlerMapping> handlerMappings是在实例化过程中赋值的。

// 如下是一些静态常量,默认类加载时已赋值
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
// 下面是请求属性部分key
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
// DispatcherServlet.properties 里面可以配置了一些策略接口的默认值,
//比如 HandlerMapping=RequestMappingHandlerMapping
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet";
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
/** Store default strategy implementations. */
@Nullable
private static Properties defaultStrategies;
// 下面这些属性在实例化调用构造函数时会被重新赋值
/** Detect all HandlerMappings or just expect "handlerMapping" bean?. */
private boolean detectAllHandlerMappings = true;
/** Detect all HandlerAdapters or just expect "handlerAdapter" bean?. */
private boolean detectAllHandlerAdapters = true;
/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean?. */
private boolean detectAllHandlerExceptionResolvers = true;
/** Detect all ViewResolvers or just expect "viewResolver" bean?. */
private boolean detectAllViewResolvers = true;
/** Throw a NoHandlerFoundException if no Handler was found to process this request? *.*/
private boolean throwExceptionIfNoHandlerFound = false;
/** Perform cleanup of request attributes after include request?. */
private boolean cleanupAfterInclude = true;
// 下面这些属性将在initStrategies方法中被调用
/** MultipartResolver used by this servlet. */
@Nullable
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet. */
@Nullable
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet. */
@Nullable
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet. */
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet. */
@Nullable
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet. */
@Nullable
private List<ViewResolver> viewResolvers;
private boolean parseRequestPath;

我们下面要分析的就是诸如viewResolvers、handlerExceptionResolvers等在何时被实例化并注入。

② 继承树


如下所示是DispatcherServlet的继承树示意图,其继承自FrameworkServlet,而FrameworkServlet又继承了HttpServletBean并实现了ApplicationContextAware接口。


HttpServletBean 则继承了HttpServlet 并实现 EnvironmentCapable, EnvironmentAware接口。所以本质上DispatcherServlet是一个httpservlet,当实例化bean时会调用其init方法。



项目启动时并不会对DispatcherServlet进行初始化,只有当第一次发起请求时才会触发DispatcherServlet Bean的实例化,进而触发其init方法。我们就从其init方法入手分析。


首先会调用DispatcherServlet的无参构造方法,这里会为其成品变量赋值。

public DispatcherServlet() {
  super();
  setDispatchOptionsRequest(true);
}

然后会调用其父类GenericServlet的init(ServletConfig config)方法,其子类HttpServletBean重写了父类GenericServlet的void init()方法,方法如下所示主要是获取初始化参数为servlet 成员赋值,然后调用initServletBean方法。但是这是一个空方法,留给子类FrameworkServlet实现。


web容器在实例化servlet时会调用Servlet的init(ServletConfig)方法,由于其子类GenericServlet实现了该方法,因而最终会调用GenericServlet.init(ServletConfig)方法。

GenericServlet.init(ServletConfig)方法内部会调用其init()方法,由于该方法又是由其子类HttpServletBean实现,实际上是执行了HttpServletBean.init()方法。

HttpServletBean.init()方法内部会调用其自定义的initServletBean()方法,但该方法实际上是由FrameworkServlet实现,实际上是执行了FrameworkServlet.initServletBean()方法。

FrameworkServlet.initServletBean()方法内部真正初始化了applicationContext,并加载配置文件,最终调用其自定义的initFrameworkServlet()方法。

@Override
public final void init() throws ServletException {
  // Set bean properties from init parameters.
  // 获取DispatcherServlet的初始化参数
  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);
      // 为bean属性赋值
      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.
  initServletBean();
}

我们继续看下FrameworkServlet的initServletBean方法。

核心代码只有两句,在servlet 属性被赋值后调用,初始化servlet容器也就是springmvc的容器(本文这里不是springboot环境,


initFrameworkServlet是个空方法,没有实现,这里核心就是initWebApplicationContext方法。

@Override
protected final void initServletBean() throws ServletException {
   // 比如Initializing Spring DispatcherServlet 'DispatcherServlet'
  getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
  if (logger.isInfoEnabled()) {
    logger.info("Initializing Servlet '" + getServletName() + "'");
  }
  long startTime = System.currentTimeMillis();
  try {
    this.webApplicationContext = initWebApplicationContext();
    initFrameworkServlet();
  }
  //...
}

③ 内部类ContextRefreshListener

如下所示其实现了ApplicationListener接口,对ContextRefreshedEvent事件感兴趣。也就是说如果应用广播了ContextRefreshedEvent事件,那么其方法FrameworkServlet.this.onApplicationEvent(event);将会被调用。

// FrameworkServlet.ContextRefreshListener
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
  @Override
  public void onApplicationEvent(ContextRefreshedEvent event) {
    FrameworkServlet.this.onApplicationEvent(event);
  }
}

那么流程就很清晰了,当ContextRefreshedEvent事件被广播出来时就会调用这里onApplicationEvent方法,继而触发onRefresh(event.getApplicationContext());–initStrategies(context);进行基础策略对象的实例化。

public void onApplicationEvent(ContextRefreshedEvent event) {
  this.refreshEventReceived = true;
  synchronized (this.onRefreshMonitor) {
    onRefresh(event.getApplicationContext());
  }
}
@Override
protected void onRefresh(ApplicationContext context) {
  initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
  initMultipartResolver(context);
  initLocaleResolver(context);
  initThemeResolver(context);
  initHandlerMappings(context);
  initHandlerAdapters(context);
  initHandlerExceptionResolvers(context);
  initRequestToViewNameTranslator(context);
  initViewResolvers(context);
  initFlashMapManager(context);
}

ContextRefreshedEvent 事件是上下文刷新事件,该事件是在容器初始化过程中被广播出来的,下面我们分析这个容器初始化过程。


【2】容器初始化

① initWebApplicationContext


initWebApplicationContext将会初始化容器并将容器发布到servletContext中,作为其一个属性。这里首先会尝试获取rootContext (SSM环境下指的是spring容器),作为springMVC容器的parent双亲IOC容器。


该方法不会创建springMVC容器,而是委派给createWebApplicationContext创建。

protected WebApplicationContext initWebApplicationContext() {
// 获取rootContext将会作为springmvc容器的parent
// root 容器存在于ServletContex中
  WebApplicationContext rootContext =
      WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  WebApplicationContext wac = null;
// 如果构造函数注入了webApplicationContext ,那么将会作为springMVC容器
  if (this.webApplicationContext != null) {
    // A context instance was injected at construction time -> use it
    wac = this.webApplicationContext;
// 如果是ConfigurableWebApplicationContext类型,且容器未激活,
//则尝试设置父类然后配置、刷新容器
    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);
      }
    }
  }
  // 尝试从servlet context寻找容器,
  //如果找到就认为该容器已经设置了parent并配置、刷新过
  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
    wac = findWebApplicationContext();
  }
  // 实际创建容器的方法
  if (wac == null) {
    // No context instance is defined for this servlet -> create a local one
    wac = createWebApplicationContext(rootContext);
  }
// 如果refreshEventReceived为false,也就是没有刷新过,那么手动调用onRefresh进行刷新
  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) {
      onRefresh(wac);
    }
  }
// 作为servletContext属性将自身放入
  if (this.publishContext) {
    // Publish the context as a servlet context attribute.
//org.springframework.web.servlet.FrameworkServlet.CONTEXT.DispatcherServlet  
// DispatcherServlet 是本文这里配置的servlet nam      
    String attrName = getServletContextAttributeName();
    getServletContext().setAttribute(attrName, wac);
  }
  return wac;
}

代码解释如下:


① 根据属性ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE(WebApplicationContext.class.getName() + ".ROOT")尝试获取rootContext,其将作为springMVC容器的parent;

② 如果当前对象的webApplicationContext不为null,也就是说实例化时通过有参构造函数注入了webApplicationContext,那么其将作为springMVC容器;

③ 如果容器是ConfigurableWebApplicationContext类型,其未激活,那么尝试设置parent,并调用configureAndRefreshWebApplicationContext进行配置、刷新。

④ 如果容器为null,那么尝试根据其成员变量contextAttribute的值作为属性从ServletContext中获取;默认contextAttribute为null。

⑤ 如果wac为null,也就是springmvc此时仍旧为null,那么调用createWebApplicationContext方法进行创建;

⑥ 如果没有执行过刷新,也就是refreshEventReceived为false,那么这里加锁调用onRefresh方法。在起onApplicationEvent方法中,该属性将会变为true。

⑦ 如果publishContext为true,那么将当前容器发布到ServletContext中作为其属性。属性key为:FrameworkServlet.class.getName() + ".CONTEXT."+serveletName

这里的核心方法是:createWebApplicationContext和configureAndRefreshWebApplicationContext。前者进行创建,后置进行配置、刷新、发布事件等等操作。


② createWebApplicationContext

我们继续看下FrameworkServlet的createWebApplicationContext方法。

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 获取class,本文这里默认是XmlWebApplicationContext
  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);
  // 设置environment
  wac.setEnvironment(getEnvironment());
  // 设置parent
  wac.setParent(parent);
  // 设置配置文件路径
  String configLocation = getContextConfigLocation();
  if (configLocation != null) {
    wac.setConfigLocation(configLocation);
  }
  // 关键核心步骤
  configureAndRefreshWebApplicationContext(wac);
  return wac;
}

代码逻辑很清晰,获取contextClass,然后进行实例化、设置environment、parent、configLocation等,最后调用configureAndRefreshWebApplicationContext对容器进行配置并刷新。


分析到这里,还没有看到诸如viewResolvers、handlerExceptionResolvers等被实例化的影子,那么肯定就在configureAndRefreshWebApplicationContext方法里面了。


③ configureAndRefreshWebApplicationContext

这里配置的servlet-nameDispatcherServlet

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());
    }
  }
  // 设置web 上下文--这是Tomcat的哦
  wac.setServletContext(getServletContext());
  // 设置上下文配置
  wac.setServletConfig(getServletConfig());
  // 设置namespace 比如getServletName() + DEFAULT_NAMESPACE_SUFFIX
  wac.setNamespace(getNamespace());
// 添加上下文刷新监听器,ContextRefreshListener对ContextRefreshedEvent事件感兴趣
  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) {
  // 设置ServletContextPropertySource ServletConfigPropertySource
    ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
  }
  // 容器的后置处理
  postProcessWebApplicationContext(wac);
  // 将容器交给ApplicationContextInitializer
  applyInitializers(wac);
  // 刷新容器
  wac.refresh();
}

方法逻辑解释如下:


① 设置id,如org.springframework.web.context.WebApplicationContext:/DispatcherServlet;

② 设置servletContext引用,这是属于web server的根容器,这里是Tomcat的容器;

③ 设置servletConfig

④ 设置namespace 比如getServletName() + DEFAULT_NAMESPACE_SUFFIX

⑤ 添加上下文刷新监听器,ContextRefreshListener对ContextRefreshedEvent事件感兴趣;

⑥ 获取ConfigurableEnvironment ,并设置ServletContextPropertySource 、ServletConfigPropertySource

⑦ postProcessWebApplicationContext对容器进行后置处理,该方法默认为空,没有实现。容器的修操作交给ApplicationContextInitializer。

⑧ applyInitializers将容器交给一系列ApplicationContextInitializer初始化器

⑨ wac.refresh()刷新容器


④ refresh刷新容器

AbstractApplicationContext的refresh是个典型的模板方法,由众多核心方法组成。

@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    // Prepare this context for refreshing.
    prepareRefresh();
    // Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);
    try {
      // Allows post-processing of the bean factory in context subclasses.
      postProcessBeanFactory(beanFactory);
      StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
      // Invoke factory processors registered as beans in the context.
      invokeBeanFactoryPostProcessors(beanFactory);
      // Register bean processors that intercept bean creation.
      registerBeanPostProcessors(beanFactory);
      beanPostProcess.end();
      // Initialize message source for this context.
      initMessageSource();
      // Initialize event multicaster for this context.
      initApplicationEventMulticaster();
      // Initialize other special beans in specific context subclasses.
      onRefresh();
      // Check for listener beans and register them.
      registerListeners();
      // Instantiate all remaining (non-lazy-init) singletons.
      finishBeanFactoryInitialization(beanFactory);
      // Last step: publish corresponding event.
      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();
      contextRefresh.end();
    }
  }
}

本文这里我们不关心每一个方法,我们只看initApplicationEventMulticaster(初始化广播器)、registerListeners();(注册监听器)以及finishRefresh(发布事件)。其他方法我们留在分析spring容器初始化过程时展开。


我们详细分析了事件广播机制与监听器、广播器的注册。这里我们不再赘述直接看finishRefresh();方法。


如下所示在publishEvent(new ContextRefreshedEvent(this));会广播事件ContextRefreshedEvent给所有的监听器。那么FrameworkServlet的内部类ContextRefreshListener的onApplicationEvent方法将会被触发。

protected void finishRefresh() {
  // Clear context-level resource caches (such as ASM metadata from scanning).
  clearResourceCaches();
  // Initialize lifecycle processor for this context.
  initLifecycleProcessor();
  // Propagate refresh to lifecycle processor first.
  getLifecycleProcessor().onRefresh();
  // Publish the final event.
  publishEvent(new ContextRefreshedEvent(this));
  // Participate in LiveBeansView MBean, if active.
  if (!IN_NATIVE_IMAGE) {
    LiveBeansView.registerApplicationContext(this);
  }
}

⑤ SpringBoot环境下

在SpringBoot环境下会将创建的根容器赋予WebApplicationContext wac,不会创建两个容器。


如下图所示FrameworkServlet的initWebApplicationContext方法会触发onRefresh方法,这个会跳转到DispatcherServlet的onRefresh方引起策略对象的初始化。



【3】策略对象初始化

当事件ContextRefreshedEvent被广播出来时,最终会触发下面initStrategies方法进行策略对象的初始化。

protected void initStrategies(ApplicationContext context) {
  initMultipartResolver(context);
  initLocaleResolver(context);
  initThemeResolver(context);
  initHandlerMappings(context);
  initHandlerAdapters(context);
  initHandlerExceptionResolvers(context);
  initRequestToViewNameTranslator(context);
  initViewResolvers(context);
  initFlashMapManager(context);
}


方法解释如下所示:


① 初始化MultipartResolver,主要用来处理文件上传;

② 初始化区域解析器,主要用来解析区域信息应用于国际化场景;

③ 初始化主题解析器,默认是FixedThemeResolver;

④ 初始化处理器映射器,这个很关键哦;

⑤ 初始化处理器适配器,这个很关键哦;

⑥ 初始化异常解析器,这个很关键哦;

⑦ 初始化请求视图名称转换器,默认是DefalutRequestToViewNameTranslator;

⑧ 初始化视图解析器;

⑨ 初始化闪存属性管理器。


① initMultipartResolver

这里MULTIPART_RESOLVER_BEAN_NAME=multipartResolver,默认将会获取得到一个CommonsMultipartResolver实例。如果实例化失败,将会抛出NoSuchBeanDefinitionException异常。

private void initMultipartResolver(ApplicationContext context) {
  try {
    this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
    //...
  }
}

② initLocaleResolver


这里LOCALE_RESOLVER_BEAN_NAME=localeResolver,默认将会获取得到一个AcceptHeaderLocaleResolver实例。如果实例化失败,将会抛出NoSuchBeanDefinitionException异常。

private void initLocaleResolver(ApplicationContext context) {
  try {
    this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
 //...
  }
}

③ initThemeResolver

这里THEME_RESOLVER_BEAN_NAME=themeResolver,默认将会获取得到一个FixedThemeResolver实例。如果实例化失败,将会抛出NoSuchBeanDefinitionException异常。

private void initThemeResolver(ApplicationContext context) {
  try {
    this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
    //...
  }
}

④ initHandlerMappings

如下代码所示,默认情况下是从BeanFactory中查找所有已经注册的HandlerMapping。

private void initHandlerMappings(ApplicationContext context) {
  this.handlerMappings = null;
  // 是否检测所有handlermappings? 默认true
  if (this.detectAllHandlerMappings) {
// 从容器中获取所有的HandlerMapping,如viewControllerHandlerMapping
    Map<String, HandlerMapping> matchingBeans =
        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    if (!matchingBeans.isEmpty()) {
      this.handlerMappings = new ArrayList<>(matchingBeans.values());
      // 对handlerMappings 按照order值 进行排序
      AnnotationAwareOrderComparator.sort(this.handlerMappings);
    }
  }
  else {
    try {
    // 获取一个bean name为handlerMapping的实例
    // HANDLER_MAPPING_BEAN_NAME = "handlerMapping"
      HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
      this.handlerMappings = Collections.singletonList(hm);
    }
    catch (NoSuchBeanDefinitionException ex) {
      // Ignore, we'll add a default HandlerMapping later.
    }
  }
// 如果handlerMappings  仍旧为null,则从默认策略配置中获取
  if (this.handlerMappings == null) {
// 尝试从DispatcherServlet.properties获取HandlerMapping对应的value,进行实例化
    this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    if (logger.isTraceEnabled()) {
      logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
          "': using default strategies from DispatcherServlet.properties");
    }
  }
// 遍历处理HandlerMapping 
  for (HandlerMapping mapping : this.handlerMappings) {
  // 判断当前handlermapping的patternParser是否为null
    if (mapping.usesPathPatterns()) {
      this.parseRequestPath = true;
      break;
    }
  }
}

Spring 5带来 PathPatternParser,用于解析URI模板模式。PathPatternParser将路径分成PathElements的链接列表。这一链条PathElements采取的是PathPattern的模式快速匹配的类。


AnnotationAwareOrderComparator.sort(this.handlerMappings);表示根据handlermapping的order属性值进行排序。排序前:


其中beanName为viewControllerHandlerMapping对应的实际bean是SimpleUrlHandlerMapping,通常由 <mvc:view-controller path="/" view-name="index"></mvc:view-controller>配置生成。


排序后:



最后一个SimpleUrlHandlerMapping则是默认拦截所有然后交给springmvc的默认处理器。

⑤ initHandlerAdapters

initHandlerAdaptersd的过程与initHandlerMappings过程一致:

  • ① 检测容器中所有的HandlerAdapter,得到实例并排序;
  • ② 如果①没有得到,则尝试获取一个beanName为handlerAdapter的实例;
  • ③ 如果②仍旧没有得到,则使用默认策略获取HandlerAdapter
private void initHandlerAdapters(ApplicationContext context) {
  this.handlerAdapters = null;
  if (this.detectAllHandlerAdapters) {
    // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
    Map<String, HandlerAdapter> matchingBeans =
        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
    if (!matchingBeans.isEmpty()) {
      this.handlerAdapters = new ArrayList<>(matchingBeans.values());
      // We keep HandlerAdapters in sorted order.
      AnnotationAwareOrderComparator.sort(this.handlerAdapters);
    }
  }
  else {
    try {
      HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
      this.handlerAdapters = Collections.singletonList(ha);
    }
    catch (NoSuchBeanDefinitionException ex) {
      // Ignore, we'll add a default HandlerAdapter later.
    }
  }
// 使用默认策略获取handlerAdapters 
  if (this.handlerAdapters == null) {
    this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
    if (logger.isTraceEnabled()) {
      logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
          "': using default strategies from DispatcherServlet.properties");
    }
  }
}

如下所示是得到的HandlerAdapter(排序后的):

⑥ initHandlerExceptionResolvers

initHandlerExceptionResolvers的过程与initHandlerMappings过程一致:


① 检测容器中所有的HandlerExceptionResolver,得到实例并排序;

② 如果①没有得到,则尝试获取一个beanName为handlerExceptionResolver的实例;

③ 如果②仍旧没有得到,则使用默认策略获取handlerExceptionResolvers

private void initHandlerExceptionResolvers(ApplicationContext context) {
  this.handlerExceptionResolvers = null;
  if (this.detectAllHandlerExceptionResolvers) {
    // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
    Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
        .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
    if (!matchingBeans.isEmpty()) {
      this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
      // We keep HandlerExceptionResolvers in sorted order.
      AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
    }
  }
  else {
    try {
      HandlerExceptionResolver her =
          context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
      this.handlerExceptionResolvers = Collections.singletonList(her);
    }
    catch (NoSuchBeanDefinitionException ex) {
      // Ignore, no HandlerExceptionResolver is fine too.
    }
  }
  // Ensure we have at least some HandlerExceptionResolvers, by registering
  // default HandlerExceptionResolvers if no other resolvers are found.
  if (this.handlerExceptionResolvers == null) {
    this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
    if (logger.isTraceEnabled()) {
      logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
          "': using default strategies from DispatcherServlet.properties");
    }
  }
}

⑦ initRequestToViewNameTranslator

如下所示,从容器中获取一个beanNameviewNameTranslator的实例。这里得到的是DefaultRequestToViewNameTranslator

private void initRequestToViewNameTranslator(ApplicationContext context) {
  try {
    this.viewNameTranslator =
        context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
    //...
  }
}

⑧ initViewResolvers

initViewResolvers的过程与initHandlerMappings过程一致:


  • ① 检测容器中所有的s,得到实例并排序;
  • ② 如果①没有得到,则尝试获取一个beanName为viewResolver的实例;
  • ③ 如果②仍旧没有得到,则使用默认策略获取viewResolvers
private void initViewResolvers(ApplicationContext context) {
  this.viewResolvers = null;
  if (this.detectAllViewResolvers) {
    // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
    Map<String, ViewResolver> matchingBeans =
        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
    if (!matchingBeans.isEmpty()) {
      this.viewResolvers = new ArrayList<>(matchingBeans.values());
      // We keep ViewResolvers in sorted order.
      AnnotationAwareOrderComparator.sort(this.viewResolvers);
    }
  }
  else {
    try {
      ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
      this.viewResolvers = Collections.singletonList(vr);
    }
    catch (NoSuchBeanDefinitionException ex) {
      // Ignore, we'll add a default ViewResolver later.
    }
  }
  // Ensure we have at least one ViewResolver, by registering
  // a default ViewResolver if no other resolvers are found.
  if (this.viewResolvers == null) {
    this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
    if (logger.isTraceEnabled()) {
      logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
          "': using default strategies from DispatcherServlet.properties");
    }
  }
}

⑨ initFlashMapManager


如下所示,从容器中获取一个beanName为flashMapManagerd的实例。这里默认得到一个SessionFlashMapManager。SessionFlashMapManager提供了在session中检索和更新flashMap的方法。

private void initFlashMapManager(ApplicationContext context) {
  try {
    this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
  //...
  }
}

⑩ getDefaultStrategies

在spring-webmvc包中默认配置了DispatcherServlet.properties,用以在获取不到策略接口的情况下,实例化一个默认值。

//路径
org\springframework\spring-webmvc\5.3.1\spring-webmvc-5.3.1.jar!\org\springframework\web\servlet\DispatcherServlet.properties
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
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.method.annotation.RequestMappingHandlerMapping,\
  org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
  org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
  org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
  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会根据入参中的Class<T> strategyInterface名称作为key获取对应的value,然后遍历实例化并放到List返回。

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
  if (defaultStrategies == null) {
    try {
      // Load default strategy implementations from properties file.
      // This is currently strictly internal and not meant to be customized
      // by application developers.
      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
      throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }
  }
  String key = strategyInterface.getName();
  String value = defaultStrategies.getProperty(key);
  if (value != null) {
    String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
    List<T> strategies = new ArrayList<>(classNames.length);
    for (String className : classNames) {
      try {
        Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
        Object strategy = createDefaultStrategy(context, clazz);
        strategies.add((T) strategy);
      }
      catch (ClassNotFoundException ex) {
        throw new BeanInitializationException(
            "Could not find DispatcherServlet's default strategy class [" + className +
            "] for interface [" + key + "]", ex);
      }
      catch (LinkageError err) {
        throw new BeanInitializationException(
            "Unresolvable class definition for DispatcherServlet's default strategy class [" +
            className + "] for interface [" + key + "]", err);
      }
    }
    return strategies;
  }
  else {
    return Collections.emptyList();
  }
}

如下所示,则是DispatcherServlet初始化完的状态:

而这些组件也被称之为SpringMVC的九大组件。DispatcherServlet正是依赖于这九大组件才完成了请求的处理。

multipartResolver = {StandardServletMultipartResolver@9333} 
localeResolver = {AcceptHeaderLocaleResolver@9340} 
themeResolver = {FixedThemeResolver@9348} 
handlerMappings = {ArrayList@9398}  size = 6
handlerAdapters = {ArrayList@9421}  size = 4
handlerExceptionResolvers = {ArrayList@9426}  size = 2
viewNameTranslator = {DefaultRequestToViewNameTranslator@9432} 
flashMapManager = {SessionFlashMapManager@9468} 
viewResolvers = {ArrayList@9438}  size = 6


目录
相关文章
|
24天前
|
前端开发 Java Spring
Spring MVC 是如何对对象参数进行校验的
【6月更文挑战第4天】对象参数校验是使用 SpringMVC 时常用的功能,这篇文章尝试分析了,Spring 是如何实现这一功能的。
31 5
|
2月前
SpringMVC 域对象共享数据
SpringMVC 域对象共享数据
19 0
|
2月前
SpringMVC之获取请求参数和域对象共享数据
【1月更文挑战第18天】 一、SpringMVC获取请求参数 1、通过ServletAPI获取 2、通过控制器方法的形参获取请求参数 3、@RequestParam 4、@RequestHeader 5、@CookieValue 6、通过POJO获取请求参数 7、解决获取请求参数的乱码问题 二、域对象共享数据 1、使用ServletAPI向request域对象共享数据 2、使用ModelAndView向request域对象共享数据 3、使用Model向request域对象共享数据 4、使用map向request域对象共享数据 5、使用ModelMap向request域对象共享数据
80 0
|
2月前
【SpringMVC】SpringMVC方式,向作用域对象共享数据(ModelAndView、Model、map、ModelMap)
【SpringMVC】SpringMVC方式,向作用域对象共享数据(ModelAndView、Model、map、ModelMap)
43 1
|
2月前
|
Java Spring
SpringMVC控制层private方法中出现注入的service对象空指针异常
一、现象 SpringMVC中controller里的private接口中注入的service层的bean为null,而同一个controller中访问修饰符为public和protected的方法不会出现这样的问题。 controller中的方法被AOP进行了代理,普通Controller如果没有AOP,private方法中bean也是正常的。
|
8月前
|
Java Spring
springMVC中获取request 对象
springMVC中获取request 对象
|
9月前
|
存储 JavaScript 前端开发
SpringMVC 域对象共享数据
SpringMVC 域对象共享数据
SpringMVC学习(五):向request域对象共享数据的五种方法
SpringMVC学习(五):向request域对象共享数据的五种方法
111 0
SpringMVC学习(五):向request域对象共享数据的五种方法
|
前端开发 Java API
Spring MVC框架:第二章:视图解析器和@RequestMapping注解使用在类级别及获取原生Servlet API对象
Spring MVC框架:第二章:视图解析器和@RequestMapping注解使用在类级别及获取原生Servlet API对象
257 0