Spring mvc 上下文初始化過程

為什么80%的碼農都做不了架構師?>>> ??hot3.png

在軟件開發的中,如果某些特性的使用比較普遍,那么這些特性往往可以作為平臺特性來實現,通過對這些平臺特性進行有效的封裝,使其向其他應用開放。正是如此,Spring由于其IOC、AOP、事務處理、持久化驅動等特點,使得其起到了一個應用平臺的作用。Spring MVC是Spring的一個重要的模塊,其web應用的實現,是由Spring的來支撐的,Spring MVC的是實現也是依托再Spring平臺提供的基礎特性的。本文主要是介紹Spring mvc容器初始化的過程,從中可以看出Spring MVC的對于Spring的依賴。

一、從Web應用角度看Spring MVC 在Servlet模型中,請求-響應的實現依賴于兩大元素的共同配合:

  1. 配置Servlet及其映射關系(在web.xml中)
  2. 在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容器對這些實現類進行管理,具體如何使用,由應用程序本身來決定。

29101532_3BuB.jpg

上圖是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的屬性注入進來。

29101405_3Vvu.jpg

DispatcherServlet也是一個Servlet,根據Servlet規范的定義,Servlet中的兩大核心方法init方法和service方法:

  1. init方法

在整個系統啟動時運行,且只運行一次。因此,在init方法中我們往往會對整個應用程序進行初始化操作。這些初始化操作可能包括對容器(WebApplicationContext)的初始化、組件和外部資源的初始化等等。

  1. service方法

在整個系統運行的過程中處于偵聽模式,偵聽并處理所有的Web請求。因此,在service及其相關方法中,我們看到的則是對Http請求的處理流程。

這篇文章主要是介紹Spring mvc 容器的初始化,所以主要是介紹iDisipatchServlet的init方法。

29101405_txfz.jpg

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 上下初始化流程圖

29101532_GTj2.jpg

參考:

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

轉載于:https://my.oschina.net/luanwu/blog/1837479

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/277734.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/277734.shtml
英文地址,請注明出處:http://en.pswp.cn/news/277734.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

經典七大排序算法

經典排序算法在面試中占有很大的比重&#xff0c;也是基礎&#xff0c;為了未雨綢繆&#xff0c;在寒假里整理并用Python實現了七大經典排序算法&#xff0c;包括冒泡排序&#xff0c;插入排序&#xff0c;選擇排序&#xff0c;希爾排序&#xff0c;歸并排序&#xff0c;快速排…

誰能給我講講原理——視頻彈幕游戲!!

舍友在一個叫BliBli的視頻網站上找到這樣一個視頻彈幕游戲&#xff0c;說實話我當時一看真的驚呆了。 從來沒有見過這種能夠互動的、充滿游戲性的視頻&#xff0c;用戶WASD可以控制飛機移動躲避字幕&#xff0c;撞到字幕左上角死亡次數還可以計數&#xff0c;字幕還并不是單一…

使用BCH 操作碼的三個新型應用程序

在BCH升級之后的一個多月里&#xff0c;許多開發人員借助重新啟用的代碼進行了相關應用的開發和完善&#xff0c;比如一些類似memo和blockpress的社交軟件可以允許用戶以一種連鎖的方式提交與BCH協議綁定的數據。最近&#xff0c;有一個名為Chainfeed的應用程序&#xff0c;將所…

一段三次分拆的螞蟻搬家式MySQL遷移經歷

趁機房搬遷的機會&#xff0c;打算做一次業務整合。現有的架構是在2010年規劃并運營起來的&#xff0c;隨著時間的推移&#xff0c;項目也越來越多。打開Nginx配置文件&#xff0c;有四十多行Include包含存在&#xff0c;每一個包含就是一個項目&#xff08;有些是Web&#xff…

6.5 scp:遠程文件復制

scp命令 用于在不同的主機之間復制文件&#xff0c;它采用SSH協議來保證復制的安全性。scp命令每次都是全量完整復制&#xff0c;因此效率不高&#xff0c;適合第一次復制時使用&#xff0c;增量復制建議使用rsync命令替代。scp [option] [[user]host1&#xff1a;]file …

Adobe——我欠你一個正版

昨天&#xff0c;2014年9月24日&#xff0c;Adobe公司宣布關閉中國研發分公司。微博截圖如下。 不知道為什么自己看到這個微博&#xff0c;心里很不舒服&#xff0c;一方面是因為Adobe中國研發分公司的關閉&#xff0c;勢必會影響中國設計和研發人才的培養&#xff0c;公司解散…

“云計算的前世今生·從阿里看云計算”內蒙古師范大學劉晨旭博士專題報告會順利召開...

6月29日下午4點&#xff0c;內蒙古師范大學阿里云大數據學院邀請阿里云產品團隊專家劉晨旭博士在學術報告廳做題為《云計算的前世今生——從阿里看云計算》的專題報告分享&#xff0c;此次活動吸引了500多名師生參加&#xff0c;兩層的報告廳里座無虛席。在此次活動中&#xff…

Mac OS使用技巧之十四:自定義文件圖標

剩下的教程多是以前遺漏掉的方法&#xff0c;和一些使用的小技巧&#xff0c;做一些補充&#xff0c;希望能幫到大家。 自定義圖標對于Mac OSX用戶來說&#xff0c;Dashboard&#xff0c;Dock欄&#xff0c;壁紙以及各種鍵盤觸摸板的快捷操作都是可以高度DIY的東西。但可能許多…

1-3.監督學習(supervised learning)

定義&#xff1a;監督學習指的就是我們給學習算法一個數據集&#xff0c;這個數據集由“正確答案”組成&#xff0c;然后運用學習算法&#xff0c;算出更多的正確答案。術語叫做回歸問題 【監督學習可分為】&#xff1a;回歸問題、分類問題。兩種 例&#xff1a;一個學生從波特…

github最值得收藏的Bootstrap3后臺管理框架

github上9款最值得收藏的bootstrap3后臺管理平臺html框架 AdminLTE Gentelella Admin Vali Admin ModularAdmin Metis Ace Light Bootstrap Dashboard Material Dashboard Clearmin 1. AdminLTE AdminLTE是一個完全響應的后臺管理模板。基于Bootstrap3框架。高度可定制&#xf…

Mac OS使用技巧之十五:快捷方便的Mini Dock

Mini Dock是前面忘記了提&#xff0c;這里做一些補充。Mini Dock是Mac OSX的一個值得大書特書的亮點。雖然windows下也有類似的東西&#xff0c;但Mac下卻提供了更為全面的功能&#xff0c;通過Mini Dock欄&#xff0c;可以快速切換、隱藏、關閉正在運行的APP。這也就比之前講過…

linux下的SSHD被連接端口修改

連接別人&#xff1a;vim /etc/ssh/ssh_config 被連接&#xff1a; vim /etc/ssh/sshd_config 端口重啟生效&#xff1a; /etc/init.d/sshd restart 轉載于:https://www.cnblogs.com/gered/p/10871335.html

Mac OS使用技巧之十六:系統失去響應怎么辦?

再好的系統&#xff0c;再快的本本&#xff0c;也會在運行時因為種種原因出現卡頓或者死機等失去響應的情況。Mac用戶也會時不時碰到這種情況&#xff0c;最常見的表現為鼠標變為七彩圓圈&#xff0c;通常等上一會兒系統會自己恢復。如果遲遲沒有響應的話&#xff0c;那就需要來…

Unity Api集合

延遲重復調用方法&#xff0c; 1 方法名 2 幾秒后開始調用 3 再次重復調用的隔了多少時間InvokeRepeating(methodName:string, time:float, repeatRate:float):void; 復制代碼取消調用 CancelInvoke("SpawnerInstance"); 復制代碼利用委托來賦值一個方法進去&#xf…

單例模式--工廠模式

單例模式又稱為職責模式&#xff0c;它用來在程序中創建一個單一功能的訪問點&#xff0c;通俗地說就是實例化出來的對象是唯一的。所有的單例模式至少擁有以下三種公共元素&#xff1a;1. 它們必須擁有一個構造函數&#xff0c;并且必須被標記為private2. 它們擁有一個保存類的…

wpfのuri(讓你完全明白wpf的圖片加載方式以及URI寫法)

原文:wpfのuri&#xff08;讓你完全明白wpf的圖片加載方式以及URI寫法&#xff09;絕對 pack WPF URI pack://application:,,,/是協議&#xff1b;“&#xff0c;&#xff0c;&#xff0c;”是“///”的變體 1.資源文件 — 本地程序集 Uri uri new Uri("pack://applicati…

Mac OS使用技巧十七:豐富多彩的花哨輸入法

OSX Mavericks中的漢字輸入功能&#xff0c;絲毫不遜色于windows&#xff0c;甚至提供了強大的手寫輸入功能和語音輸入功能&#xff0c;并且發展到現在&#xff0c;已經有很多種第三方輸入法支持Mac了。 一、基本的輸入法首先說一下支持Mac的各種中文輸入法&#xff0c;其實我覺…

語言-漢語:漢語

ylbtech-語言-漢語&#xff1a;漢語漢語&#xff0c;即漢族的傳統語言&#xff0c;是中國通用語言&#xff0c;國際通用語言之一&#xff0c;屬漢藏語系&#xff0c;與藏語、壯語、侗語、黎語、彝語、苗語、瑤語等都是親屬語言。漢語歷史悠久&#xff0c;使用人數最多&#xff…

Duboo入門示例(Idea開發環境)

在學習Dubbo分布式框架時的官方入門例子&#xff0c;很有代表性。簡單清晰。 有關Dubbo的概念、概述和簡單的配置文件&#xff0c;可以看官方文檔的簡述 會很快對Duboo有個整體的概念。 準備工作: 下載示例&#xff0c;點擊這里下載&#xff0c;建議用git管理。下載注冊中心&am…

Mac OS使用技巧十八:Safari碉堡功能之一制作Widget

Safari的使用大家應該自己摸索就可以慢慢駕輕就熟&#xff0c;畢竟再高端也是個瀏覽器&#xff0c;從開始上網就要一直使用瀏覽器&#xff0c;Safari只是眾多瀏覽器中的一個比較強大的罷了。下面給大家介紹一下Safari的一個碉堡隱藏功能!!!!&#xff08;其實不算隱藏啦。。。在…