前面我们分析了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-name
是DispatcherServlet
。
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
如下所示,从容器中获取一个beanName
为viewNameTranslator
的实例。这里得到的是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