Spring做爲一個優秀的web框架,其運行是基於Tomcat的。在咱們前面的講解中,Spring的驅動都是使用的ClassPathXmlApplicationContext
,而且都是直接在main方法中啓動的,可是在Tomcat容器中,咱們是沒法使用main方法的,於是其驅動方式必然與咱們測試時不同。Tomcat是一個基於Servlet規範的web容器,而Spring則提供了對Servlet規範的支持,其DispatcherServlet
則是Servlet規範的具體實現。於是在web開發過程當中,當咱們啓動Tomcat容器時其會根據Servlet規範啓動Spring實現的DispatcherServlet
,這樣也就驅動了Spring的運行。本文主要從源碼的角度講解Spring在web容器中是如何初始化的。java
在配置web容器時,咱們都會配置一個web.xml,而在配置web.xml時,最主要的兩個組件就是ContextLoaderListener
和DispatcherServlet
的配置。以下是一個典型的web.xml文件的配置:web
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>myservlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>myservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
這裏ContextLoaderListener
的做用是對對Servlet Context的行爲進行監聽,其實現了ServletContextListener
接口,這個接口聲明以下:spring
public interface ServletContextListener extends EventListener { // 用於在Servlet Context初始化事前執行 default public void contextInitialized(ServletContextEvent sce) {} // 用於在Servlet Context被銷燬以後執行 default public void contextDestroyed(ServletContextEvent sce) {} }
這裏ServletContextListener
是Servlet規範中提供的一個接口,該接口中的contextInitialized()
會在servlet context初始化以前執行,而contextDestroyed()
方法則會在servlet context被銷燬以後執行。Spring提供的ContextLoaderListener
對這兩個方法都進行了實現。實際上,在web.xml中指定的contextConfigLocation參數的解析就是在ContextLoaderListener.contextInitialized()
方法中解析的,也就是說Spring對於bean的建立其實是在Servlet Context初始化以前就已經完成了。mvc
web.xml中配置的DispatcherServlet
則是Servlet規範中HttpServlet的一個具體實現,實現了該接口的以後該類就具備處理web請求的能力了。這裏能夠看到,DispatcherServlet
配置攔截的url是'/',也就是說全部的web請求都會通過DispatcherServlet
,而對於具體的url的處理,其實是在DispatcherServlet
中進行分發的。這也就是Spring爲何只須要配置一個Servlet的緣由。app
關於DispatcherServlet
的配置這裏不得不提的是,咱們得爲其提供一個myservlet-servlet.xml的配置文件,用於只爲當前servlet提供Spring的一些基本配置。這裏該文件的命名必須按照servlet名稱-servlet.xml
這種格式進行,因爲咱們的servlet的名稱爲myservlet,於是配置文件名必須爲myservlet-servlet.xml。若是使用者須要自定義文件名,能夠在當前servlet中使用init-param
標籤進行配置,如:框架
<servlet> <servlet-name>myservlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/myservlet-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
另外,對於配置的myservlet-servlet.xml文件的初始化,是在DispatcherServlet.init()
方法中進行的。這裏須要注意的是,myservlet-servlet.xml完徹底全是一個獨立的Spring配置文件,咱們能夠在其中聲明Spring的bean,而且註冊到Spring容器中。ide
在web.xml中咱們提到了兩個Spring的配置文件,一個是咱們經常使用的applicationContext.xml,另外一個專屬於某個Servlet的myservlet-servlet.xml。兩個配置文件的初始化分別是由ServletContextListener.contextInitialized()
方法和GenericServlet.init()
方法進行的。這兩個方法都是Servlet規範中提供的初始化方法,兩個方法分別會初始化兩個Spring容器,這兩個容器中applicationContext.xml對應的容器會做爲myservlet-servlet.xml初始化的容器的父容器而存在,於是在myservlet-servlet.xml的容器中,咱們是可使用任何在applicationContext.xml中聲明的bean的,可是反過來則不行。在處理具體請求的時候,咱們所使用的Spring容器其實一直都是myservlet-servlet.xml聲明而來的。測試
對於ContextLoaderListener,其主要是用於初始化咱們經常使用的applicationContext.xml的。以下是其源碼:ui
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { // 初始化Spring容器 public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } // 銷燬Spring容器 public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
能夠看到,這裏對於Spring容器的初始化是委託給了initWebApplicationContext()
方法進行的,以下是該方法的源碼:this
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 若是當前已經初始化過一個web application context則拋出異常,這樣能夠保證一個web容器中 // 只會有一個web application context if (servletContext.getAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application" + " context present - check whether you have multiple ContextLoader*" + " definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { if (this.context == null) { // 經過servlet配置建立一個WebApplicationContext對象 this.context = createWebApplicationContext(servletContext); } // 這裏在createWebApplicationContext()方法中會保證建立的WebApplicationContext本質上是 // ConfigurableWebApplicationContext類型的,於是這裏進行類型判斷的時候是可以進入if分支的 if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // 若是當前WebApplicationContext沒有初始化過就對其進行初始化 if (!cwac.isActive()) { // 若是當前WebApplicationContext沒有父ApplicationContext,則經過 // loadParentContext()方法加載一個,該方法其實是一個空方法,這裏提供 // 出來只是爲了方便用戶進行容器屬性的自定義,由於父容器的內容會繼承到子容器中 if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } // 對WebApplicationContext進行配置,而且調用其refresh()方法初始化 // 配置文件中配置的Spring的各個組件 configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 初始化完成以後將當前WebApplicationContext設置到ServletContext中 servletContext.setAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); // 設置當前WebApplicationContext的類加載器 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext" + " attribute with name [" + WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
能夠看到,對於WebApplicationContext的初始化,Spring首先會根據配置文件配置建立一個WebApplicationContext對象,而後判斷該對象是否初始化過,若是沒有,則對其進行配置而且初始化。這裏咱們首先看看Spring是如何建立WebApplicationContext對象的:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 讀取配置文件中配置的實現了WebApplicationContext接口的類 Class<?> contextClass = determineContextClass(sc); // 判斷讀取到的類是否實現了ConfigurableWebApplicationContext接口,若是沒實現則拋出異常 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } // 經過反射實例化WebApplicationContext對象 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } // 這個方法的主要做用在於讀取配置文件中配置的實現了WebApplicationContext接口的類, // 從而做爲WebApplicationContext容器。這裏讀取配置文件的方式有兩種:①讀取web.xml中配置的 // contextClass屬性,若是存在則將其做爲WebApplicationContext容器;②讀取Spring提供的 // ContextLoader.properties屬性文件中配置的WebApplicationContext容器。 protected Class<?> determineContextClass(ServletContext servletContext) { // 讀取用戶在web.xml中使用contextClass屬性自定義的WebApplicationContext容器, // 若是不爲空,則直接返回 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { // 若是用戶沒有自定義WebApplicationContext,則經過defaultStrategies讀取 // ContextLoader.properties屬性文件中配置的WebApplicationContext, // 這裏讀取到的具體實現類就是XmlWebApplicationContext contextClassName = defaultStrategies .getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }
這裏講到,咱們能夠在web.xml中配置自定義的WebApplicationContext,具體的配置方式就是在web.xml中配置以下屬性:
<context-param> <param-name>contextClass</param-name> <param-value>mvc.config.MyXmlWebApplicationContext</param-value> </context-param>
經過這種方式咱們就能夠實現自定義的WebApplicationContext。對於Spring提供的默認WebApplicationContext實現,其是經過defaultStrategies這個屬性讀取的,這個屬性的初始化是在ContextLoader(ContextLoaderListener繼承了該類)中使用static代碼塊進行初始化的,讀者可自行查閱。
在建立了WebApplicationContext對象以後,Spring會對其進行配置和各個組件的初始化,以下是ContextLoader.configureAndRefreshWebApplicationContext()
方法的具體實現:
protected void configureAndRefreshWebApplicationContext( ConfigurableWebApplicationContext wac, ServletContext sc) { // 判斷當前WebApplicationContext是否具備統一的id,若是沒有,首先會從web.xml中讀取, // 具體的使用contextId屬性進行制定,該屬性的配置方式與上面的contextClass一致,若是沒有, // 則經過默認規則聲明一個 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } // 獲取web.xml中配置的contextConfigLocation屬性值,這裏也就是咱們前面配置的 // applicationContext.xml,在後面調用refresh()方法時會根據xml文件中的配置 // 初始化Spring的各個組件 wac.setServletContext(sc); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // 獲取當前Spring的運行環境,而且初始化其propertySources ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } // 這裏customizeContext()方法是一個空方法,供給用戶自定義實現ContextLoaderListener時 // 對WebApplicationContext進行自定義 customizeContext(sc, wac); // 這裏refresh()方法用於讀取上面聲明的配置文件,而且初始化Spring的各個組件 wac.refresh(); }
關於WebApplicationContext的配置和初始化,這裏主要分爲了四個步驟:①爲當前WebApplicationContext聲明一個id,用於對其進行惟一標識;②讀取web.xml中配置的Spring配置文件的位置;③初始化propertySources;④讀取Spring配置文件中的內容,而且實例化Spring的各個組件。這裏須要說明的是,對於Spring各個組件的初始化,調用的是ConfigurableWebApplicationContext.refresh()
方法,這個方法咱們前面講解Spring bean註冊解析時已經講解了,讀者能夠翻閱Spring Bean註冊解析(一)和Spring Bean註冊解析(二)。
在ConfigurableWebApplicationContext.refresh()
方法調用完成以後,Spring配置文件中的各項配置就都已經處理完成。如此,ContextLoaderListener
的初始化工做也就完成。
對於DispatcherServlet的初始化,這裏須要注意的是,在web.xml中咱們配置了load-on-startup
標籤,配置了該標籤就表示當前Servlet的初始化方法會在web容器啓動完成後調用,也就是這裏的DispatcherServlet.init()
方法。咱們首先看看該方法的源碼:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // 讀取在web.xml中經過init-param標籤設置的屬性,若是沒有配置,這裏pvs就會是empty的 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { // 註冊Resource對象對應的PropertyEditor BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 初始化BeanWrapper對象,這裏是一個空方法,供給使用者對BeanWrapper進行自定義處理 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; } } // 初始化當前DispatcherServlet的各項配置 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
能夠看到,這裏對DispatcherServlet的初始化主要分爲兩個步驟:①判斷當前Servlet中使用使用init-param標籤自定義了屬性,若是定義了,則將其設置到BeanWrapper中;②初始化DispatcherServlet。這裏咱們繼續閱讀initServletBean()
的源碼:
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { // 初始化當前servlet配置的Spring配置 this.webApplicationContext = initWebApplicationContext(); // 這裏initFrameworkServlet()方法是一個空方法,供給用戶對當前servlet對應的Spring容器 // 進行自定義的處理 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
這裏initServletBean()
除了進行一些日誌記錄之外,主要工做仍是委託給了initWebApplicationContext()
方法進行,咱們這裏直接閱讀該方法的源碼:
protected WebApplicationContext initWebApplicationContext() { // 獲取在ContextLoaderListener中初始化的Spring容器,而且將其做爲當前servlet對應 // 的容器的父容器,這樣當前servlet容器就可使用其父容器中的全部內容了 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 若是當前Servlet對應的WebApplicationContext不爲空,而且其未被初始化,則對其進行初始化 if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 判斷當前WebApplicationContext是否已初始化過,沒有則進行初始化 if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } // 初始化當前WebApplicationContext configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 若是wac爲空,則說明當前servlet對應的WebApplicationContext是空的, // 這裏會經過當前servlet配置的contextAttribute屬性查找一個自定義的 // WebApplicationContext,將其做爲當前servlet的容器 wac = findWebApplicationContext(); } if (wac == null) { // 若是用戶沒有自定義WebApplicationContext,則建立一個,而且對其進行初始化 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 這裏的onRefresh()方法並非初始化Spring配置文件中的bean的, // 而是用於初始化Spring處理web請求相關的組件的,如RequestMappingHandlerMapping等 onRefresh(wac); } if (this.publishContext) { // 將當前WebApplicationContext對象設置到ServletContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
這裏默認狀況下,DispatcherServlet中是不存在已經初始化過的WebApplicationContext的,於是最終仍是會調用createWebApplicationContext()方法進行初始化,在初始化完成以後就會初始化Spring處理web請求的相關組件。咱們首先看createWebApplicationContext()方法的實現:
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { // 讀取web.xml中配置的contextClass屬性,將其做爲當前servlet的WebApplicationContext Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } // 保證用戶定義的WebApplicationContext對象是ConfigurableWebApplicationContext類型的 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"); } // 實例化WebApplicationContext對象 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); // 設置當前的運行環境 wac.setEnvironment(getEnvironment()); // 將ContextLoaderListener中初始化的WebApplicationContext做爲當前 // WebApplicationContext的父容器 wac.setParent(parent); // 獲取當前servlet配置的contextConfigLocation String configLocation = getContextConfigLocation(); if (configLocation != null) { wac.setConfigLocation(configLocation); } // 讀取當前WebApplicationContext配置的Spring相關的bean,並進行初始化 configureAndRefreshWebApplicationContext(wac); return wac; }
這裏對當前servlet的WebApplicationContext的初始化過程其實比較簡單,其中最主要須要注意的有兩點:①會將ContextLoaderListener初始化的WebApplicationContext做爲當前WebApplicationContext的父容器;②在獲取當前configLocation的時候,若是沒有設置,則使用"servlet名稱-servlet.xml"的方式讀取。
在第三點最後,咱們講到,初始化servlet對應的容器以後,其會調用onRefresh()方法初始化Spring web相關的組件,該方法的具體實如今DispatcherServlet.onRefresh()
方法中,這裏咱們直接閱讀該方法的源碼:
@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); }
這裏對Spring的九大組件的實例化方式都比較統一,關於這九大組件的具體細節咱們後面會依次進行講解。這裏咱們主要講解其初始化方式。關於這九大組件,其實例化方式可分爲兩類:
對於第一種實例化方式,咱們這裏以LocaleResolver的初始化爲例進行講解,以下是initLocaleResolver()方法的源碼:
private void initLocaleResolver(ApplicationContext context) { try { // 這裏LOCALE_RESOLVER_BEAN_NAME的值爲localeResolver,也就是說用戶若是 // 須要自定義的LocaleResolver,那麼在聲明該bean是,其名稱必須爲localeResolver this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // 若是Spring容器中沒有配置自定義的localeResolver,則經過默認策略實例化對應的bean this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver + "]"); } } }
對於第二種方式,咱們這裏以HandlerMapping的實例化爲例進行講解,以下是initHandlerMappings()方法的實現原理:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // 檢查是否配置了獲取Spring中配置的全部HandlerMapping類型對象,是則進行讀取,而且按照 // 指定的排序規則對其進行排序,不然就從Spring中讀取名稱爲handlerMapping的bean, // 並將其做爲指定的bean if (this.detectAllHandlerMappings) { // 從Spring容器中讀取全部的實現了HandlerMapping接口的bean Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // 對獲取到的HandlerMapping進行排序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 獲取Spring容器中名稱爲handlerMapping的bean HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // 忽略當前異常 } } if (this.handlerMappings == null) { // 若是上述方式無法獲取到對應的HandlerMapping,則使用默認策略獲取對應的HandlerMapping this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
上述兩種初始化Spring web組件的方式中都涉及到一個獲取默認的bean的方法,該方法其實是從Spring提供的配置文件指定對應的bean的Class,在讀取該文件以後會對其進行實例化,而後返回。對於getDefaultStrategies()方法的實現原理,其實比較簡單,咱們這裏主要給你們展現Spring提供的組件的配置文件的內容,該配置文件的名稱爲DispatcherServlet.properties
,以下是該文件的內容:
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.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.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
能夠看到,這裏的配置文件的key就是對應的接口的全路徑名,這也就是getDefaultStrategies()方法第二個參數傳入的是Class對象的緣由,而value就是該接口對應的實現類,能夠有多個。
本文首先講解了web.xml文件的配置方式,而且着重講解了該文件中各個配置的意義,接着依次在源碼的層面對web.xml中配置的各個組件的初始化方式進行了講解。