在軟件開發的中,若是某些特性的使用比較廣泛,那麼這些特性每每能夠做爲平臺特性來實現,經過對這些平臺特性進行有效的封裝,使其向其餘應用開放。正是如此,Spring因爲其IOC、AOP、事務處理、持久化驅動等特色,使得其起到了一個應用平臺的做用。Spring MVC是Spring的一個重要的模塊,其web應用的實現,是由Spring的來支撐的,Spring MVC的是實現也是依託再Spring平臺提供的基礎特性的。本文主要是介紹Spring mvc容器初始化的過程,從中能夠看出Spring MVC的對於Spring的依賴。html
1、從Web應用角度看Spring MVC 在Servlet模型中,請求-響應的實現依賴於兩大元素的共同配合:java
項目規模擴大以後,請求-響應的映射關係所有定義在web.xml中,將形成web.xml的不斷膨脹而變得難以維護。針對這個問題,SpringMVC提出的方案就是:提煉一個核心的Servlet覆蓋對全部Http請求的處理。這一被提煉出來的Servlet,一般被咱們稱之爲:核心分發器。在SpringMVC中,核心分發器就是org.springframework.web.servlet.DispatcherServlet。web
核心分發器要解決的是下面兩個問題:spring
問題1:核心Servlet應該可以創建起一整套完整的對全部Http請求進行規範化處理的流程。編程
問題2:核心Servlet應該可以根據必定的規則對不一樣的Http請求分發到不一樣的Servlet對象上去進行處理。json
針對上面的這個兩個問題,SpringMVC的解決方案是:將整個處理流程規範化,並把每個處理步驟分派到不一樣的組件中進行處理。服務器
處理流程規範化 :將處理流程劃分爲若干個步驟(任務),並使用一條明確的邏輯主線將全部的步驟串聯起來 處理流程組件化 : 將處理流程中的每個步驟(任務)都定義爲接口,併爲每一個接口賦予不一樣的實現模式session
處理流程規範化的首要內容就是考慮一個通用的Servlet響應程序大體應該包含的邏輯步驟:mvc
對Http請求進行初步處理,查找與之對應的Controller處理類(方法) 調用相應的Controller處理類(方法)完成業務邏輯 對Controller處理類(方法)調用時可能發生的異常進行處理 根據Controller處理類(方法)的調用結果,進行Http響應處理 所謂的組件化,實際上也就是使用編程語言將這些邏輯語義表達出來。在Java語言中,最適合表達邏輯處理語義的語法結構是接口,而接口能夠有不一樣的實現,所以上述的四個流程也就被定義爲了四個不一樣接口,它們分別是:app
HandlerMapping HandlerAdapter HandlerExceptionResolver ViewResolver 2、從Spring角度看Spring MVC 從上面能夠看出,組件是核心分發器(DispatchServlet)的核心所在,它們是http請求處理的邏輯載體,DispatcherServlet是邏輯處理的調度中心,組件則是被調度的操做對象。而Spring容器在這裏所起到的做用,是協助DispatcherServlet更好地對組件進行管理。
咱們知道,SpringMVC的組件是一個個的接口定義,當咱們在SpringMVC的核心配置文件中定義一個組件時,使用的倒是組件的實現類,用具體的實現類來指定組件的行爲模式,不一樣的實現類表明了不一樣的行爲模式,它們在Spring中是能夠共存的。Spring容器對這些實現類進行管理,具體如何使用,由應用程序自己來決定。
上圖是Spring官方reference中的一幅圖,DispatchServlet對外接收http的請求,而請求的處理的是依靠組件來完成的,組件的接口實現的是依靠Spring IOC容器(WebApplicationContext)來管理。從這個圖中咱們能夠看出,Spring MVC實現web應用是依賴與Spring提供的基礎特性(IOC等)。圖中的兩個WebApplicationContext的區別,留到下面再講。
3、Spring MVC 入口配置文件web.xml Spring mvc 有哪些配置文件:
入口配置文件:web.xml;由web或應用服務器爲每一個web項目加載的配置文件。 應用上下文:包括web框架特有配置文件:SpringMVC的${dispatcherServletName}-servlet.xml配置文件。和Spring的配置文件applicationContext.xml,applicationContext-*.xml。 遵循servlet規範,Spring MVC的web應用的入口配置文件也是web.xml。web容器的初始化首先是加載web.xml文件,Jetty在啓動時首先加載此配置文件,而且對其中定義的listener和servlet等進行相應的加載和初始化。Jetty並不清楚(也不關心)其餘配置文件的存在,所以,加載其餘配置文件應該是你(框架)的事情。那麼怎麼加載呢?前面說過Jetty在啓動時會自動加載和初始化listener和servlet,那麼咱們能夠自定義一個listener(ContextLoaderListener)或servlet(DispatcherServlet),Jetty會根據web.xml加載和初始化他們,而他們則負責加載相應的配置文件。
在web.xml配置文件中,有兩個主要的配置:ContextLoaderListener和DispatcherServlet。一樣的關於spring配置文件的相關配置也有兩部分:context-param和DispatcherServlet中的init-param。那麼,這兩部分的分別表示是Spring 容器的初始化和web容器的初始化。DispatcherServlet和ContextLoaderListener提供了在Web容器中對spring的接口。ServletContext爲Spring的IOC容器提供了一個宿主環境,在宿主環境中,Spring MVC創建起了一個IOC容器體系。
下面看一下Spring mvc 中web.xml文件的相關配置內容:
push-center-server test webAppRootKey test contextConfigLocation classpath:applicationContext.xml
<context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j2.xml</param-value> </context-param> <!-- 項目使用的配置文件位置.項目啓動自動讀取 --> <context-param> <param-name>propertiesConfigLocation</param-name> <param-value>/WEB-INF/conf/config.properties</param-value> </context-param> <!--jmonitor--> <context-param> <param-name>jmonitor-configfile</param-name> <param-value>jmonitor.properties</param-value> </context-param> <listener> <listener-class>com.meituan.jmonitor.servlet.ContextListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> <!--jmonitor http collector--> <filter> <filter-name>JMonitorHttpMonitorFilter</filter-name> <filter-class>com.meituan.jmonitor.collector.http.HttpMonitorFilter</filter-class> </filter> <filter-mapping> <filter-name>JMonitorHttpMonitorFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>com.sankuai.meituan.web.MtDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:webmvc-config.xml</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
4、Spring IOC容器(根上下文)的初始化 Spring Framework自己沒有Web功能,Spring MVC使用WebApplicationContext接口擴展ApplicationContext,使得擁有web功能,WebApplicationContext接口默認的實現是XmlWebApplicationContext。那麼,Spring MVC是如何在web環境中建立IoC容器呢?
先看一下WebApplicationContext的源碼,
WebApplicationContext
public interface WebApplicationContext extends ApplicationContext { //用於在ServletContext中存取根上下文 String ROOTWEBAPPLICATIONCONTEXTATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
String SCOPE_REQUEST = "request"; String SCOPE_SESSION = "session"; String SCOPE_GLOBAL_SESSION = "globalSession"; String SCOPE_APPLICATION = "application"; String SERVLET_CONTEXT_BEAN_NAME = "servletContext"; String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters"; String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes"; //取得當前web容器的ServletContext ServletContext getServletContext();
}
在Spring MVC中,Spring Context是以父子的繼承結構存在的。Web環境(ServletContext)中存在一個根上下文,這個Context是整個應用的根上下文,是其餘context的雙親Context。同時Spring MVC也對應的持有一個獨立的Context,它是根上下文的子上下文。
由ContextLoaderListener首先啓動的上下文爲根上下文,該上下文是與ServletContext相伴而生的,在根上下文的基礎上,Spring MVC對應持有的一個用來管理控制器須要的對象的子上下文。下圖是ContextLoaderListener繼承關係,它實現了ServletContextListener接口,這個接口提供了與Servlet生命週期結合的回調,好比contextInitialized和contextDestroyed方法。創建WebApplicationContext的過程是在contextInitialized的接口實現中完成的,具體的載入IOC容器的過程是由ContextLoader來完成的。
ContextLoaderListener
`public class ContextLoaderListener extends ContextLoader implements ServletContextListener { private ContextLoader contextLoader;
public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); } [@Deprecated](https://my.oschina.net/jianhuaw) protected ContextLoader createContextLoader() { return null; } [@Deprecated](https://my.oschina.net/jianhuaw) public ContextLoader getContextLoader() { return this.contextLoader; } public void contextDestroyed(ServletContextEvent event) { if (this.contextLoader != null) { this.contextLoader.closeWebApplicationContext(event.getServletContext()); } ContextCleanupListener.cleanupAttributes(event.getServletContext()); }
}`
從ContextLoaderListener源碼能夠看出,實現的是ServletContextListener接口,這個接口裏的函數會結合Web容器的生命週期被調用。由於ServletContextListener是ServletContext的監聽者,當servletContext啓動或者中止的時候,會觸發響應的事件,監聽器ServletContextListener會接收到事件,會作出相應的響應,好比這裏的contextInitialized方法和contextDestroyed方法。Spring IOC容器(根上下文)的生成與銷燬就是經過這個兩個方法的,因此根上下文是與ServletContext相伴而生的。
因此當Web應用啓動時,contextInitialized方法會執行載入根上下文(IOC容器),具體過程是首先從Servlet事件中獲得ServletContext,而後以ServletContext爲宿主環境,載入根上下文(IOC容器),具體的載入過程是由ContextLoader處理的。
下圖所示爲根上下文的加載過程,下面將結合源碼來看一下這個過程是如何實現的。
根上下文的載入過程:
ROOT Context是在ContextLoaderListener中配置的,ContextLoaderListener讀取context-param中的contextConfigLocation指定的配置文件,建立ROOT Context。下面看一下ContextLoaderListener中建立context的源碼:
ContextLoader加載根上下文的源碼
//這裏開始對WebApplicationContext進行初始化 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //在整個web應用中,只能有一個根上下文,判斷ServletContext中是否已經有根上下文 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 { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { // 執行了建立WebApplicationContext的操做 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; 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 -> // determine parent for root web application context, if any. //載入根上下文的雙親上下文 ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } //將根上下文放置在servletContext中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 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; } } protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 根據web.xml中的配置,決定用那種WebApplicationContext,默認用XmlWebApplicationContext Class<?> contextClass = determineContextClass(sc); //判斷contextClass是否繼承ConfigurableWebApplicationContext或者是其接口實現 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } //直接實例化須要產生的IOC容器 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } //設置IOC容器各個參數,而後經過refresh啓動容器的初始化 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { //設置application context ID 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 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // Servlet <= 2.4: resort to name specified in web.xml, if any. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName())); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } } //設置ServletContext wac.setServletContext(sc); //設置配置文件的位置參數 String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter != null) { wac.setConfigLocation(initParameter); } //在refresh以前,根據配置的config locations從新定製(Customize)ConfigurableWebApplicationContext customizeContext(sc, wac); //啓動容器的初始化 wac.refresh(); }
從上面的源碼中能夠看出來,IOC容器在web容器中的啓動過程,與在應用中啓動IOC容器的方式類似,不一樣的是這裏須要考慮web容器的環境的特色,好比各類參數的設置,IOC容器與web容器ServletContext的結合等。在初始化上下文之後,該上下文被存儲再ServletContext中,這樣就創建了一個全局的關於整個應用的上下文,即所謂的根上下文。同時在啓動Spring MVC的時候,DispatchServlet在進行本身持有的上下文的初始化時,是將此根上下文設置爲本身的雙親上下文的。
http://blog.csdn.net/and1kaney/article/details/51214193 這篇文章能夠看到根容器初始化過程的整個的call hierarchy。
5、Spring MVC容器(子上下文)的初始化 以上是web容器中根上下文的加載與初始化,在完成對ContextLoaderListener的初始化之後,web容器開始初始化DispatchServlet,DispatchServlet會創建本身的上下文來管理Spring MVC的bean對象。在創建這個本身持有的上下文的時候,會從ServletContext中獲得根上下文做爲DispatchServlet持有的上下文的雙親上下文,再對本身持有的上下文進行初始化,最後把本身持有的這個上下文也保存到ServletContext中。
咱們先看下DispatchServlet的繼承關係,以下圖。DispatchServlet經過繼承FrameworkServlet和HttpServletBean而繼承了HttpServlet。HttpServletBean是Spring對於Servlet最低層次的抽象。在這一層抽象中,Spring會將這個Servlet視做是一個Spring的bean,並將web入口配置文件web.xml中DispatchServlet定義的init-param參數中的值做爲bean的屬性注入進來。
DispatcherServlet也是一個Servlet,根據Servlet規範的定義,Servlet中的兩大核心方法init方法和service方法:
在整個系統啓動時運行,且只運行一次。所以,在init方法中咱們每每會對整個應用程序進行初始化操做。這些初始化操做可能包括對容器(WebApplicationContext)的初始化、組件和外部資源的初始化等等。
在整個系統運行的過程當中處於偵聽模式,偵聽並處理全部的Web請求。所以,在service及其相關方法中,咱們看到的則是對Http請求的處理流程。
這篇文章主要是介紹Spring mvc 容器的初始化,因此主要是介紹iDisipatchServlet的init方法。
HttpServletBean
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { //讀取web.xml中DispatchServlet定義中的<init-param>,對Bean屬性進行配置 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); //生成一個BeanWrapper,將當前的這個Servlet類轉化爲一個BeanWrapper,從而可以以Spring的方式來對init-param的值進行注入 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); //將init-param中的值注入 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // 調用子類的initServletBean進行具體的初始化 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
FrameworkServlet則是在HttpServletBean的基礎之上的進一步抽象。經過FrameworkServlet真正初始化了一個Spring的容器(WebApplicationContext),並引入到Servlet對象之中:
FrameworkServlet
/** * Overridden method of {@link HttpServletBean}, invoked after any bean properties * have been set. Creates this servlet's WebApplicationContext. */ @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 { //對Spring的容器(webApplicationContext)進行初始化 this.webApplicationContext = initWebApplicationContext(); 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"); } } protected WebApplicationContext initWebApplicationContext() { //這裏調用WebApplicationContextUtils靜態類來從ServletContext中獲得根上下文,使用這個根上下文做爲當前MVC上下文的雙親上下文。 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // 若是一個context的實例被注入了,直接用 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // 若是此上下文尚未初始化,就設置上下文的參數,如雙親上下文、application context id等 if (cwac.getParent() == null) { // 若是被注入的context實例沒有雙親上下,則將根上下文設置爲其雙親上下文 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 //沒有注入的context實例,這裏從ServletContext中查找是否有context實例已經註冊到了servlet context了 wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one //在ServletContext沒有context實例,因此須要建立一個WebApplicationContext,以根上下文爲雙親下文建立 wac = createWebApplicationContext(rootContext); } //刷新上下文(執行組件的初始化),這個方法由子類DispatchServlet的方法實現 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. onRefresh(wac); } //把當前的上下文存到ServletContext中去,使用的屬性名是和當前的Servlet名相關的 if (this.publishContext) { // Publish the context as a servlet context attribute. 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; } //建立上下文 protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { // 根據web.xml中的配置,決定用那種WebApplicationContext,默認用XmlWebApplicationContext 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 + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //設置上下文,並啓動初始化 wac.setEnvironment(getEnvironment()); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); configureAndRefreshWebApplicationContext(wac); return wac; }
在這個調用關係中,能夠看到MVC的初始化是再DispatchServlet的initStrategies方法中完成的,包括對各類MVC框架的實現元素,好比支持國際化的LocaleResolver、支持request映射的HandlerMappings、以及視圖生成的ViewResolver等的初始化。對於具體實現元素的初始化就不一一列出源碼了,這裏以HandlerMappings爲例來講明MVC框架元素的初始化過程。
DispatchServlet
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } /** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ ////初始化默認的Spring Web MVC框架使用的策略(如HandlerMapping) protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; //這裏導入全部的HandlerMapping Bean,這些Bean能夠是在當前的DispatchServlet的IOC容器,也能夠是其雙親上下文中的,這裏detectAllHandlerMappings默認是爲true的,從全部的IOC容器中取 if (this.detectAllHandlerMappings) { // 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<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.sort(this.handlerMappings); } } else { //從當前IOC容器中經過getBean獲取handlerMapping try { 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,設置默認的handlerMapping,默認值設置在DispatcherServlet.properties中 // 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); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
6、Spring MVC 上下初始化流程圖
參考:
http://blog.csdn.net/c289054531/article/details/9196149
http://blog.csdn.net/prince2270/article/details/5889117
http://blog.arganzheng.me/posts/config-file-in-web-application.html