【Spring源碼解析】—— 結合SpringMVC過程理解IOC容器初始化


關於IOC容器的初始化,結合之前SpringMVC的demo,對其過程進行一個相對詳細的梳理,主要分為幾個部分:

 

一、IOC的初始化過程,結合代碼和debug過程重點說明

1、 為什么要debug?

答:直接自己從源碼看第一遍,會有一個初步的認識;但是看完之后,會因為沒有實際走一遍執行而無法驗證自己認知的過程;另外就是:因為基於接口本身會有多個實現,因此在很多情況下,通過Ctrl+B直接會進入到接口中,但是方法的實現體為空,通過Ctrl+Alt+B查看具體實現,會出現多個類都實現了這個方法,具體是哪一個,無法准確確定,容易導致理解失誤

 

2、 初始化的階段及對應調用到的方法

從SpringMVC的demo中的web.xml配置項可以看到:

<listener>
    <listener-class>

               org.springframework.web.context.ContextLoaderListener

</listener-class>
</listener>

從ContextLoaderListener進入,這是入口,之后會經過refresh()過程,refresh()過程中會將IOC容器初始化完成,其中重點有:load階段、process階段、register階段;接下來會從入口以及后續的三個階段進行介紹。

 

二、詳解

入口部分:

進入到ContextLoaderListener類,ContextLoaderListener實現了ServletContextListener接口,該接口中有兩個default方法,分別是:

//接收Web應用程序初始化過程正在啟動的通知

default public void contextInitialized(ServletContextEvent sce) {}

//接收ServletContext即將關閉的通知

default public void contextDestroyed(ServletContextEvent sce) {}

ContextLoaderListener類中未保持原有的default,重新做了實現:

/**
 * Initialize the root web application context.
 */
@Override
public void contextInitialized(ServletContextEvent event) {
   initWebApplicationContext(event.getServletContext());
}

然后進入到父類ContextLoader類的initWebApplicationContext方法中,該方法中會繼續通過:configureAndRefreshWebApplicationContext

最終調用到:AbstractApplicationContext中的refresh(),至此進入到IOC的load階段(下方為abstractApplicationContext的類圖)

 

load過程:refresh()

進入到重要邏輯分析 ——》ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

一步步往里走,到AbstractRefreshableApplicationContext中,具體如下:
protected final void refreshBeanFactory() throws BeansException {
   //判斷是否已經有beanFactory了,有則銷毀
   if (hasBeanFactory()) {
      //銷毀Beans
      destroyBeans();
      //將BeanFactory的所有相關內容全部置為null,其中包含:
      //serializedID為null,並且對象引用本身置為null
      //即等GC了 
      closeBeanFactory();
   }
   try {
      //創建BeanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      //加載BeanDefinitions
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
} 

從這里的createBeanFactory()跟進去可以進入看到是new了一個DefaultListableBeanFactory

DefaultListableBeanFactory的類圖如下:

之后下一步loadBeanDefinitions(beanFactory)——》XmlWebApplicationContext類的loadBeanDefinitions

中(這里就是會有多個實現的情況出現,直接通過demo單步調試跟進確定程序走向),之后會new一個XmlBeanDefinitionReader實例,是以前面new出來的beanFactory為參數,這個xmlReaderDefinitionReader會作為參數傳入到loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中,之后一級一級進行轉換,最終進入到:AbstractBeanDefinitionReader類中的LoadDefinitions,這個時候參數經過了前面的各種過程的轉換,最終進入到了doLoadBeanDefinitions(inputSource, encodedResource.getResource())

這里做了層層封裝,每一次從外層進入到里層的具體實現和調用上,主要變化的是:參數形式,可以看到:從getConfigLocations()得到配置文件的路徑,默認路徑是:

/** Default config location for the root context. */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

再到基於得到的配置路徑,對其進行循環loaderDefinitions,在每一層循環中,是針對單一的一個String configLocation,之后將String類型的location轉換成Resource,再轉換成InputStream,之后轉成InputSource,最終在doLoadBeanDefinitions中將直接用InputSource的實例作為參數,層層遞進層層轉換:

//真正從特定XML文件中加載bean definitions的方法(這里將exception的內容沒有全部放進來)

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

      throws BeanDefinitionStoreException {



   try {

      //加載————》得到一個doc對象。其實最后一個是DOMParser類的對象

      //至此,整個過程中不斷去將File先解析之后再轉換成輸入流,再最終得到Document doc,加載完成

      Document doc = doLoadDocument(inputSource, resource);

      //注冊:具體做的事項是:

      //1、解析xml

      //2、將解析得到的BeanName和beanDefinition都存儲到beanDefinitionMap中進行注冊

      int count = registerBeanDefinitions(doc, resource);

      if (logger.isDebugEnabled()) {

         logger.debug("Loaded " + count + " bean definitions from " + resource);

      }

      return count;

   }
 

其中,會先doLoadDocument方法調用得到一個DomParser的對象;至此加載完成;然后進入到registerBeanDefinitions的過程,其中:會有process過程 + register過程

 

process過程:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   //new一個BeanDefinitionDocumentReader對象
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   int countBefore = getRegistry().getBeanDefinitionCount();
   //注冊BeanDefinitions
   //使用documentReader實例調用registerBeanDefinitions方法,具體操作內容包含:
   //(1)BeanDefinitionDocumentReader處理Document元素時,將Document元素的解析工作委托給
   // BeanDefinitionParserDelegate處理,其中會將beanName做一個唯一值的轉換,為后續的register到map中提供前提
   //(2)判斷BeanDefinitionMap中是否有某個k,v已經存在,有的話更新Definition的v值,無則直接put k,v
   //DefaultBeanDefinitionDocumentReader 實現了 BeanDefinitionDocumentReader接口
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

其中process過程在registetBeanDefinitions跟進到下一步中,即:DefaultBeanDefinitionDocumentReader類的registerBeanDefinitions方法,看到:doRegisterBeanDefinitions方法,看到這里就可以興奮一下啦,真正做事情的來了:

其中關鍵步驟就兩部分:第一部分是createDelegate並賦值給delegate字段;之后進行processxml三步曲 preprocess、parse、postprocess

protected void doRegisterBeanDefinitions(Element root) {
      //先賦值給parent,之后創建createDelegate重新賦值給delegate
   BeanDefinitionParserDelegate parent = this.delegate;

   //在DefaultBeanDefinitionDocumentReader處理Document元素時,
   // 將Document文檔內元素具體解析工作委托給BeanDefinitionParserDelegate類來處理
   this.delegate = createDelegate(getReaderContext(), root, parent);

   if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         // We cannot use Profiles.of(...) since profile expressions are not supported
         // in XML config. See SPR-12458 for details.
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isDebugEnabled()) {
               logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }

   //預處理Xml
   preProcessXml(root);
   //解析BeanDefinitions
   parseBeanDefinitions(root, this.delegate);
   //后置處理Xml
   postProcessXml(root);

   this.delegate = parent;
}

進入到parseBeanDefinitions(root, this.delegate)中,跟到具體實現中:

//處理給定的bean節點,解析bean definitions,並且通過registry進行注冊
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //這個BeanDefinitionHolder從命名上來看:
   //分析含義是:用來包裹存儲一個BeanDefinition的容器,因為叫做Holder
   //實際是:確實是用來存儲這個BeanDefinition的包裹,其中對beanName做了一個特定處理,將之轉換成了唯一值

//並且進行詳細的解析,包含:對於set、list、map等
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         //得到了一個自認為完備的beanDefinition了,然后進行注冊
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

 

register過程:

從上面代碼中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())會進入到類BeanDefinitionUtils對static方法registerBeanDefinition的調用:

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   //根據name進行注冊,因為name是唯一性的了
   //具體里面就是直接調用了map.put,將其放入到beanDefinitionMap中
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

之后跟進到registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()) 最終會進入到DefaultListableBeanFactory類中:

摘取方法中的關鍵邏輯:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

//從map中取出beanName的key對應的Definition的對象
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {

}
//為map中的beanName的key值重新設置對應的v值
this.beanDefinitionMap.put(beanName, beanDefinition);

}

其中beanDefintionMap的定義如下:

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

 

二、將以上梳理的整個過程通過時序圖的方式,更清晰地展現出調用關系:(圖片若無法看清的,可以查看大圖或者下載)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM