為什么80%的碼農都做不了架構師?>>> ??
在軟件開發的中,如果某些特性的使用比較普遍,那么這些特性往往可以作為平臺特性來實現,通過對這些平臺特性進行有效的封裝,使其向其他應用開放。正是如此,Spring由于其IOC、AOP、事務處理、持久化驅動等特點,使得其起到了一個應用平臺的作用。Spring MVC是Spring的一個重要的模塊,其web應用的實現,是由Spring的來支撐的,Spring MVC的是實現也是依托再Spring平臺提供的基礎特性的。本文主要是介紹Spring mvc容器初始化的過程,從中可以看出Spring MVC的對于Spring的依賴。
一、從Web應用角度看Spring MVC 在Servlet模型中,請求-響應的實現依賴于兩大元素的共同配合:
- 配置Servlet及其映射關系(在web.xml中)
- 在Servlet實現類中完成響應邏輯
項目規模擴大之后,請求-響應的映射關系全部定義在web.xml中,將造成web.xml的不斷膨脹而變得難以維護。針對這個問題,SpringMVC提出的方案就是:提煉一個核心的Servlet覆蓋對所有Http請求的處理。這一被提煉出來的Servlet,通常被我們稱之為:核心分發器。在SpringMVC中,核心分發器就是org.springframework.web.servlet.DispatcherServlet。
核心分發器要解決的是下面兩個問題:
問題1:核心Servlet應該能夠建立起一整套完整的對所有Http請求進行規范化處理的流程。
問題2:核心Servlet應該能夠根據一定的規則對不同的Http請求分發到不同的Servlet對象上去進行處理。
針對上面的這個兩個問題,SpringMVC的解決方案是:將整個處理流程規范化,并把每一個處理步驟分派到不同的組件中進行處理。
處理流程規范化 :將處理流程劃分為若干個步驟(任務),并使用一條明確的邏輯主線將所有的步驟串聯起來 處理流程組件化 : 將處理流程中的每一個步驟(任務)都定義為接口,并為每個接口賦予不同的實現模式
處理流程規范化的首要內容就是考慮一個通用的Servlet響應程序大致應該包含的邏輯步驟:
對Http請求進行初步處理,查找與之對應的Controller處理類(方法) 調用相應的Controller處理類(方法)完成業務邏輯 對Controller處理類(方法)調用時可能發生的異常進行處理 根據Controller處理類(方法)的調用結果,進行Http響應處理 所謂的組件化,實際上也就是使用編程語言將這些邏輯語義表達出來。在Java語言中,最適合表達邏輯處理語義的語法結構是接口,而接口可以有不同的實現,因此上述的四個流程也就被定義為了四個不同接口,它們分別是:
HandlerMapping HandlerAdapter HandlerExceptionResolver ViewResolver 二、從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的區別,留到下面再講。
三、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>
?
四、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, etcif (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,默認用XmlWebApplicationContextClass<?> 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 IDif (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 informationString 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()));}}}
//設置ServletContextwac.setServletContext(sc);
//設置配置文件的位置參數String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (initParameter != null) {wac.setConfigLocation(initParameter);}
//在refresh之前,根據配置的config locations重新定制(Customize)ConfigurableWebApplicationContextcustomizeContext(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。
五、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方法
在整個系統啟動時運行,且只運行一次。因此,在init方法中我們往往會對整個應用程序進行初始化操作。這些初始化操作可能包括對容器(WebApplicationContext)的初始化、組件和外部資源的初始化等等。
- service方法
在整個系統運行的過程中處于偵聽模式,偵聽并處理所有的Web請求。因此,在service及其相關方法中,我們看到的則是對Http請求的處理流程。
這篇文章主要是介紹Spring mvc 容器的初始化,所以主要是介紹iDisipatchServlet的init方法。
HttpServletBean
@Overridepublic 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.*/@Overrideprotected 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,默認用XmlWebApplicationContextClass<?> 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
?
@Overrideprotected 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獲取handlerMappingtry {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");}}}
六、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