IOC容器在web容器中初始化過程——(二)深刻理解Listener方式裝載IOC容器方式

先來看一下ContextServletListener的代碼web

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
    /**
      這個方法就是用來初始化web application context的
  服務器啓動時,檢測到此監聽類,系統會調用此方法。
*/ @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } /**   服務器關閉時,這個方法調用,用來銷燬容器
*/
  @Override
  public
void contextDestroyed(ServletContextEvent event) {
    closeWebApplicationContext(event.getServletContext());
    ContextCleanupListener.cleanupAttributes(event.getServletContext());
  }
}
 
初始化方法contextInitialized裏調用了父類ContextLoader的initWebApplicationContext方法
下面咱們看下initWebApplicationContext方法的代碼
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        /**
             webApplicationContext只能存在一個,若重複會拋出IllegalStateException異常
             從servletContext中獲取ApplicationContext容器;若是已經存在,則提示初始化容器失敗,檢查web.xml文件中是否認義有多個容器加載器
             ServletContext接口的簡述:public interface ServletContext
             定義了一系列方法用於與相應的servlet容器通訊,好比:得到文件的MIME類型,分派請求,或者是向日志文件寫日誌等。
             每個web-app只能有一個ServletContext,web-app能夠是一個放置有web application 文件的文件夾,也能夠是一個.war的文件。
             ServletContext對象包含在ServletConfig對象之中,ServletConfig對象在servlet初始化時提供servlet對象。
         */
        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容器爲空,則建立一個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);
                    }
                    //配置完成後,進行配置和刷新WebApplicationContext
                    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;
        }
    }

 

從上面的代碼能夠看出,咱們建立好的applicationContext容器會放在servletContext中。servletContext是什麼  呢?  
  
在web容器中,經過ServletContext爲Spring的IOC容器提供宿主環境,對應的創建起一個IOC容器的體系。其中,首先須要創建的是根上下文,這個上下文持有的對象能夠有業務對象,數據存取對象,資源,事物管理器等各類中間層對象。在這個上下文的基礎上,和web MVC相關還會有一個上下文來保存控制器之類的MVC對象,這樣就構成了一個層次化的上下文結構。
 
 
真正建立ApplicationContext的方法是在createWebApplicationContext方法中完成的。
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        // 首先決定要建立的applicationContext容器的類
        Class<?> contextClass = determineContextClass(sc);
        // 若是獲取到的類不是ConfigurableWebApplicationContext類型的,則建立容器失敗,因此這裏建立的容器必須是ConfigurableWebApplicationContext類型的

        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        //經過BeanUtils的instantiateClass方法建立
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

 

determineContextClass()方法獲取了要建立的容器類
 protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException
    {
        // 從web.xml中獲取須要初始化的容器的類名
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        // 若是獲取到的類名不爲空,則建立該容器的Class對象
        if (contextClassName != null)
        {
            try {
                return ClassUtils.forName(contextClassName);
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        // 不然建立默認的容器的Class對象,即:org.springframework.web.context.support.XmlWebApplicationContext
        // 在建立ContextLoader時,defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);這句代碼已經準備好默認的容器類
        else
        {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try
            {
                return ClassUtils.forName(contextClassName);
            }
            catch (ClassNotFoundException ex)
            {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }
    該方法首先判斷從web.xml文件的初始化參數CONTEXT_CLASS_PARAM(的定義爲public static final String CONTEXT_CLASS_PARAM = "contextClass";)獲取的的類名是否存在,若是存在,則容器的Class;不然返回默認的Class。如何獲取默認的容器Class,注意看建立contextLoader時的代碼註釋就知道了。
    由此看來,spring不只有默認的applicationContext的容器類,還容許咱們自定義applicationContext容器類,不過Spring不建義咱們自定義applicationContext容器類。
    
   在經過createWebApplicationContext()方法建立WebApplicationContext容器以後,會調用configureAndRefreshWebApplicationContext()方法爲容器初始化配置文件中定義的bean類,它的實現過程還沒搞清楚,下一章節再來作解釋(=_=)。
 
   最後放上前輩的兩張圖,幫助你們更容易理解此配置的實現原理。

 

相關文章
相關標籤/搜索