Spring源碼系列:Spring的啓動過程

Spring對於程序員說來講都不陌生;做爲一個強大的開源技術,幫助咱們可以更好的進行項目的開發與維護。 直接進入主題吧。Spring的啓動過程實際上就是Ioc容器初始化以及載入Bean的過程;本文主要是學習記錄下前半部分(Ioc容器的初始化),新手上路,若有錯誤,請指正! 1.從配置文件提及java

<listener>  
     <listener-class>org.springframework.web.context.ContextLoaderListener
     </listener-class>  
</listener> 
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>classpath:applicationContext.xml</param-value>  
</context-param> 
複製代碼

在通常的WEB項目中,項目的啓動通常是從web.xml文件的載入開始的。若是咱們的項目中使用了Spring,那麼你確定會在你的web.xml文件中看到上面的配置。Spring正是經過ContextLoaderListener監聽器來進行容器初始化的。下面經過代碼進行分析。程序員

2.Spring容器加載的三步走web

  • step1:建立一個WebApplicationContext
  • step2:配置而且刷新Bean
  • step3:將容器初始化到servlet上下文中

3.WebApplicationContext的建立過程spring

public class ContextLoaderListener extends ContextLoader implements ServletContextListener 複製代碼

從ContextLoaderListener的定義能夠看出,該監聽器繼承了ContextLoader,而且重寫了ServletContextListener中的contextInitialized和contextDestroyed方法。app

在contextInitialized中,經過調用父類(ContextLoader)的initWebApplicationContext方法進行容器建立:ide

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}
複製代碼

下面來看initWebApplicationContext的代碼:學習

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    //1:判斷當前容器是否存在,若是存在則報容器已經存在的異常信息
    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);
    //下面這個日誌就是咱們常常在啓動Spring項目時看到的日誌信息: 
    //Initializing Spring root WebApplicationContext
    //Root WebApplicationContext: initialization started
    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.
      //若是當前容器爲null,則建立一個容器,並將servletContext上下文做爲參數傳遞進去,
      if (this.context == null) {
        this.context = createWebApplicationContext(servletContext);
      }
       //判斷當前容器是否爲可配置的,只有是Configurable的容器,才能進行後續的配置
      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);
        }
      }
       //將配置而且刷新過的容器存入servlet上下文中,並以WebApplicationContext的類名做爲key值

      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;
    }
  }
複製代碼

下面咱們在看下是如何建立WebApplicationContext的this

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    //首先來肯定context是由什麼類定義的,而且判斷當前容器是否爲可配置的
    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這個方法spa

protected Class<?> determineContextClass(ServletContext servletContext) {
    //首先從web.xml中查看用戶是否本身定義了context
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    //若是有,則經過反射建立實例
    if (contextClassName != null) {
      try {
        return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
      }
      catch (ClassNotFoundException ex) {
        throw new ApplicationContextException(
            "Failed to load custom context class [" + contextClassName + "]", ex);
      }
    }
    /*若是沒有,則去defaultStrategies裏面取【defaultStrategies是Propertites類的/對象,在ContextLoader中的靜態代碼塊中初始化的;具體可看下下面的圖像】;默認容器是XmlWebApplicationContext*/
  else {
   contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
      try {
        return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
      }
      catch (ClassNotFoundException ex) {
        throw new ApplicationContextException(
            "Failed to load default context class [" + contextClassName + "]", ex);
      }
    }
  }
複製代碼

總的來講就是:Spring的web工程首先回去檢查用戶是否本身定義了context,若是有就採用;若是沒有就使用Spring默認的。 defaultStrategies初始化: debug

至此,容器建立完成。下面是整個過程的一個流程圖(有疏漏,回頭補一個時序圖):

相關文章
相關標籤/搜索