DispatcherServlet是SpringMVC的核心,因此初始化也是圍繞着他展開。在上一篇文章從Servlet提及
中講到,Servlet提供了初始化的方法init
,咱們先看一下DispatcherServlet的類圖:java
咱們從圖中所示的繼承關係,咱們去找一下哪裏實現了這個init
方法。web
GenerericServlet
實現了Servlet接口,它定義了一個通用的,無關協議的Servlet實現,咱們能夠看到它的init方法體是空的,說明他將具體的實現交給了它的子類spring
public void init() throws ServletException { // NOOP by default }
HttpServlet
繼承了GenerericServlet
,他和GenerericServlet
不同,他是一個抽象類,基於HTTP協議,提供了doGet()
doPost()
doPut()
doDelete()
等方法,這些也就是咱們熟悉的get,post,put,delete等請求方式,咱們發現他沒有覆蓋GenerericServlet
的init
方法app
HttpServletBean
,他也是一個抽象類,咱們看一下他實現的init
方法ide
public final void init() throws ServletException { // 咱們在配置web.xml時候,咱們會配置 // <init-param> // <param-name>contextConfigLocation</param-name> // <param-value>/xxxx/xxxx.xml</param-value> // </init-param> // 這個方法就是用來加載配置文件 // Set bean properties from init parameters. 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); 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(); }
到這裏,咱們能夠知道HttpServletBean
的init
方法實現了Servlet的init
方法,即DispatcherServlet初始化的入口,他又提供了initServletBean()
方法,這個方法體是空,說明須要子類去覆蓋實現。接下來咱們去找他的子類的實現post
FrameworkServlet
,一樣他是一個抽象類,繼承了HttpServletBean
,該類提供了Servlet上下文的初始化,咱們能夠看到,他同時實現了HttpServletBean
的initServletBean()
方法,咱們看一下這個方法ui
protected final void initServletBean() throws ServletException { ...... try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } ....... }
咱們先看initWebApplicationContext()
, 從方法名咱們能夠猜出,他應該是初始化上下文的,看一下他的源碼this
protected WebApplicationContext initWebApplicationContext() { // 拿到spring容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it // 若是webApplicationContext已經注入,使用它 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 // 把spring 容器做爲該servlet容器的父容器 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 // 若是webApplicationContext沒有注入,繼續查詢 wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one // 若是仍是沒有,就新建 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) { onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac;
SpringMVC中存在兩種上下文,一個是spring容器,一個是springMVC容器,spring容器做爲springMVC容器的父容器,接下來咱們一步步分析spa
獲取根容器,即Spring 容器code
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
public static WebApplicationContext getWebApplicationContext(ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { ...... Object attr = sc.getAttribute(attrName); ...... }
能夠看到,根據WebApplicationContext.class.getName() + ".ROOT"
從ServletContext中獲取,那問題來了,他是何時放進去的呢?咱們在配置web.xml的時候會配置這樣一段
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
咱們仍是看一下他的源碼,ContextLoaderListener
是ServletContextListener
的實現,咱們看下關鍵性的代碼
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { ...... servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ....... }
第一種是咱們能夠把DispatcherServlet做爲spring的bean,將SpringMVC容器注入,這裏咱們直接拿來做爲SpringMVC容器,第一個if
就是在作這事,第二種是咱們能夠把SpringMVC容器註冊到ServletContext中去,根據context id去獲取,這是第二個if
作的事
若是以上兩種狀況都不是,就本身建立SpringMVC容器
SpringMVC容器初始化成功後,咱們接下來看onRefresh(wac);
方法,他是又是一個空實現,咱們去找他的子類
protected void onRefresh(ApplicationContext context) { // For subclasses: do nothing by default. }
咱們中去來到了SpringMVC的核心DispatcherServlet
,咱們看下他的onRefresh
方法
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); }
DispatcherServlet
在這裏初始化他的組件,咱們先看一下,如下的代碼
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { 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()); } }
咱們能夠看到DispatcherServlet默認了一些策略,這裏經過classPath下的DispatcherServlet.properties
文件進行配置
咱們先看一下他的初始化代碼
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; private void initMultipartResolver(ApplicationContext context) { try { this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); ...... } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; ...... } }
MultipartResolver
用於實現SpringMVC的文件上傳功能,能夠看到要實現文件上傳功能,必須註冊MultipartResolver的bean,並且默認是不支持文件上傳的
一樣的咱們先看代碼
private void initLocaleResolver(ApplicationContext context) { try { this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); ...... } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); ...... } }
一樣的會先從容器中找LocaleResolver的bean,若是未找到,則加載默認策略,配置在classPath下的DispatcherServlet.properties
文件,org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
, LocaleResolver默認AcceptHeaderLocaleResolver,即根據request的請求頭Accept-Language獲取地區
private void initThemeResolver(ApplicationContext context) { try { // 先去找名稱爲themeResolver的bean this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class); ...... } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. //加載默認策略 this.themeResolver = getDefaultStrategy(context, ThemeResolver.class); ...... } }
先去找名稱爲themeResolver的bean,若是不存在,加載默認策略,默認爲FixedThemeResolver
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // 默認去找全部實現了HandlerMapping的bean // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. // 按照優先級排序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 只找名稱爲handlerMapping的bean 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. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { // 加載默認策略 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); ...... } }
SpringMVC默認加載全部實現了HandlerMapping
的bean,並按照優先級排序,或者只找名稱爲handlerMapping的bean,這個能夠在web.xml中經過detectAllHandlerMappings
參數進行配置,若是前兩種都沒有找到bean,則加載默認策略,默認爲BeanNameUrlHandlerMapping RequestMappingHandlerMapping RouterFunctionMapping
private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. // 默認去找全部實現了HandlerAdapter的bean 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的bean 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. } } // Ensure we have at least some HandlerAdapters, by registering // default HandlerAdapters if no other adapters are found. // 加載默認策略 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"); } } }
和HandlerMapping同樣,先去找全部的實現了HandlerAdapter的bean,或者找名稱爲handlerAdapter的bean,這個能夠在web.xml中經過detectAllHandlerAdapters
參數進行配置,都沒有再加載默認策略,默認HttpRequestHandlerAdapter SimpleControllerHandlerAdapter RequestMappingHandlerAdapter HandlerFunctionAdapter
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"); } } }
和HandlerMapping同樣,先去找全部的實現了HandlerExceptionResolver的bean,或者找名稱爲handlerExceptionResolver的bean,這個能夠在web.xml中經過detectAllHandlerExceptionResolvers
參數進行配置,都沒有再加載默認策略,默認ExceptionHandlerExceptionResolver ResponseStatusExceptionResolver DefaultHandlerExceptionResolver
private void initRequestToViewNameTranslator(ApplicationContext context) { try { this.viewNameTranslator = context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class); ...... } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class); ...... } }
先去找名稱爲viewNameTranslator的bean,若是不存在,加載默認策略,默認爲DefaultRequestToViewNameTranslator
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"); } } }
和HandlerMapping同樣,先去找全部的實現了ViewResolver的bean,或者找名稱爲viewResolver的bean,這個能夠在web.xml中經過detectAllViewResolvers
參數進行配置,都沒有再加載默認策略,默認InternalResourceViewResolver
private void initFlashMapManager(ApplicationContext context) { try { this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class); ...... } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class); ...... } }
先去找名稱爲flashMapManager的bean,若是不存在,加載默認策略,默認爲SessionFlashMapManager