這一節主要是記錄一下Spring Ioc 容器的啓動過程。java
Spring 的 Ioc 容器是怎麼被加載和使用的? web容器爲它提供了宿主環境 ServlectContext, Tomcat 啓動時會讀取web.xml。web
而且實例化web.xml中配置的ContextLoaderListener ,下面看一下ContextLoaderListener的建立過程:spring
在實例化ContextLoaderListener 以後,經過接口回調執行ContextLoaderListener 類中的contextInitialized(ServletContextEvent event)方法,該方法的做用是初始化根上下文app
(注:WebApplicationContext 是ApplicationContext 的拓展接口,而Spring默認提供的根上下文是XmlWebApplicationContext。)ide
代碼:post
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //若servletContext中存在WebApplicationContext 則拋出異常 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!"); } //…………省略 //初始化 WebApplicationContext 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) { //建立webApplication的實例 this.context =createWebApplicationContext(servletContext); } //省略代碼在下面給出 }
createWebApplicationContext(servletContext) 方法的做用是建立根上下WebApplicationContext的實例this
代碼:spa
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() +"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
這裏determineContextClass(ServletContext sc) 用於返回ApplicationContext的實現類,若是沒有在配置文件中配置自定義的ApplicationContext,默認返回 XmlWebApplicationContext ,獲得實例後對其進行檢查,知足條件則返回它的實例。日誌
createWebApplicationContext方法下面省略的代碼:code
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.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
這一步是判斷返回的根上下文實例context有沒有父上下文,取決於在web.xml中定義的參數:locatorFactorySelector,這是一個可選參數,加載完父上下文以後,執行configureAndRefreshWebApplicationContext方法,先看一下源碼:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { 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 //………省略 } wac.setServletContext(sc); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // The wac environment's #initPropertySources will be called in any case when the conte //xt // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh //………省略 wac.refresh(); }
上面的方法setServletContext 爲根上下文設置ServletContext的引用 , 執行根上下文的refresh()方法, 就是上一節說的,建立BeanFacotry的過程,執行完這方法在啓動日誌就能夠看到
信息: Loading XML bean definitions from class path resource [spring.xml]
這個時候就完成了對spring.xml資源的讀取,到此就結束了根上下文(Application)的配置與加載資源,servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context)這句代碼就是把上下文存入ServletContext當中,下次獲取根上下文的時候就能夠經過 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE屬性獲取。
到此ContextLoaderListener的建立就結束了。