1. 程式人生 > 其它 >Spring IOC原始碼(三):IOC容器之 建立容器

Spring IOC原始碼(三):IOC容器之 建立容器

1、原始碼解析

  obtainFreshBeanFactory()方法包含了 Spring的IOC容器 - DefaultListableBeanFactory物件的建立、解析配置檔案中的bean資訊載入至容器中。
1 // 建立容器物件DefaultListableBeanFactory,載入xml配置檔案的屬性值到當前工廠中
2 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

1.1、初始化容器 - refreshBeanFactory()

  refreshBeanFactory()方法由AbstractApplicationContext的子類AbstractRefreshableApplicationContext具體實現,初始化容器-beanFactory。
1 // 重新整理內部工廠
2 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
3    // 初始化BeanFactory,並讀取XML檔案,將得到的BeanFactory記錄在當前實體的屬性中
4    refreshBeanFactory();
5    // 返回當前實體的beanFactory屬性
6    return getBeanFactory();
7 }
refreshBeanFactory()的整體流程:

1、建立容器beanFactory

  建立DefaultListableBeanFactory物件的容器 - beanFactory(忽略BeanNameAware、BeanFactoryAware、BeanClassLoaderAware介面實現類);

2、為beanFactory填充屬性

  customizeBeanFactory(beanFactory)是AbstractRefreshableApplicationContext中的方法, 為beanFactory設定屬性。
 1 // 為beanFactory填充屬性
 2 protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
 3    // 如果屬性allowBeanDefinitionOverriding不為空,設定給beanFactory物件相應屬性,是否允許覆蓋同名稱的不同定義的物件
4 if (this.allowBeanDefinitionOverriding != null) { 5 beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); 6 } 7 // 如果屬性allowCircularReferences不為空,設定給beanFactory物件相應屬性,是否允許bean之間存在迴圈依賴 8 if (this.allowCircularReferences != null) { 9 beanFactory.setAllowCircularReferences(this.allowCircularReferences); 10 } 11 }
  allowBeanDefinitionOverriding 與allowCircularReferences 是在createBeanFactory(beanFactory);中做的賦值操作。allowCircularReferences 是在建立DefaultListableBeanFactory物件做的初始化,預設值為true。
1 // 是否允許名稱相同但beanDefinition不同的物件被覆蓋
2 private boolean allowBeanDefinitionOverriding = true;

3、獲取beanDefinition

  解析配置檔案中定義的標籤資訊: 預設標籤解析;自定義標籤解析。loadBeanDefinitions(beanFactory);載入bean定義資訊。

 1 // 通過XmlBeanDefinitionReader載入beanDefinition
 2 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 3    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
 4    // 建立一個xml的beanDefinitionReader,並通過回撥設定到beanFactory中
 5    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 6 
 7    // 為beanDefinitionReader設定環境物件
 8    beanDefinitionReader.setEnvironment(this.getEnvironment());
 9    beanDefinitionReader.setResourceLoader(this);
10    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
11 
12    // 初始化beanDefinitionReader物件,並設定配置檔案是否要進行驗證
13    initBeanDefinitionReader(beanDefinitionReader);
14    // 載入BeanDefinitions
15    loadBeanDefinitions(beanDefinitionReader);
16 }
  上述程式碼是AbstractXmlApplicationContext中的方法,建立了bean定義資訊讀取器,為讀取器填充屬性,最終會呼叫XmlBeanDefinitionReader中的loadBeanDefinitions()方法,核心虛擬碼如下:
 1 // 載入beanDefinition資訊
 2 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 3 
 4    // 通過屬性來記錄已經載入的資源
 5    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
 6    if (!currentResources.add(encodedResource)) {
 7       throw new BeanDefinitionStoreException(
 8             "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
 9    }
10    // 從encodedResource中獲取已經封裝的Resource物件, 再獲取Resource物件的inputStream流資訊
11    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
12       InputSource inputSource = new InputSource(inputStream);
13       if (encodedResource.getEncoding() != null) {
14          inputSource.setEncoding(encodedResource.getEncoding());
15       }
16       // 載入beanDefinition核心邏輯,實際從指定的xml中載入beanDefinition
17       return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
18    }
19    catch (IOException ex) {
20       throw new BeanDefinitionStoreException(
21             "IOException parsing XML document from " + encodedResource.getResource(), ex);
22    }
23    finally {
24       // 移除已經載入的資源
25       currentResources.remove(encodedResource);
26       if (currentResources.isEmpty()) {
27          this.resourcesCurrentlyBeingLoaded.remove();
28       }
29    }
30 }
  可以看到這一步還是載入beanDefinition的準備工作,獲取Resource物件的輸入流inputStream,封裝輸入流並進入載入beanDefinition的核心邏輯doLoadBeanDefinitions()。
 1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
 2       throws BeanDefinitionStoreException {
 3 
 4    try {
 5       // 獲取xml檔案的document物件,這個解析過程是由documentLoader完成的,
 6       Document doc = doLoadDocument(inputSource, resource);
 7       // 根據文件的節點資訊封裝成一個個的BeanDefinition物件
 8       int count = registerBeanDefinitions(doc, resource);
 9       if (logger.isDebugEnabled()) {
10          logger.debug("Loaded " + count + " bean definitions from " + resource);
11       }
12       return count;
13    }
14    .....
15    catch (Throwable ex) {
16       throw new BeanDefinitionStoreException(resource.getDescription(),
17             "Unexpected exception parsing XML document from " + resource, ex);
18    }
19 }
  該步驟主要是註冊bean定義資訊的準備工作,獲取配置檔案的document物件,通過String[](配置檔案集合) -string(配置檔案)-Resource[]- resource,最終開始將resource讀取成一個document文件。
 1 // 建立document物件的讀取器
 2 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
 3    // 對xml的beanDefinition進行解析
 4    // 建立BeanDefinitionDocumentReader物件
 5    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
 6    int countBefore = getRegistry().getBeanDefinitionCount();
 7    // 通過spring-beans的XSD或DTD實現beanDefinition具體的解析過程
 8    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
 9    return getRegistry().getBeanDefinitionCount() - countBefore;
10 }
  該步是註冊bean定義資訊的準備工作,獲取document物件讀取器。
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
2    this.readerContext = readerContext;
3    // 註冊定義在<bean/>標籤的每一個beanDefinition
4    doRegisterBeanDefinitions(doc.getDocumentElement());
5 }
  doRegisterBeanDefinitions()是將BeanDefinition物件註冊進容器的核心邏輯,核心虛擬碼如下
 1 public static final String PROFILE_ATTRIBUTE = "profile";
 2 public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
 3 
 4 // 註冊beanDefinitionn的核心處理邏輯
 5 protected void doRegisterBeanDefinitions(Element root) {
 6    BeanDefinitionParserDelegate parent = this.delegate;
 7    // 初始化解析beanDefinition的委託類
 8    this.delegate = createDelegate(getReaderContext(), root, parent);
 9    // ...
10    // 解析beanDefinition前的處理
11    preProcessXml(root);
12    // 解析beanDefinition資訊
13    parseBeanDefinitions(root, this.delegate);
14    // 解析beanDefinition後的處理
15    postProcessXml(root);
16 
17    this.delegate = parent;
18 }
  doRegisterBeanDefinitions()中的核心步驟 parseBeanDefinitions(root, this.delegate);,preProcessXml和postrocessXml有子類拓展實現,下面來看看DefaultBeanDefinitionDocumentReader中的解析方法parseBeanDefinitions()。
 1 // 解析bean定義資訊
 2 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
 3     // 是否為預設名稱空間
 4     if (delegate.isDefaultNamespace(root)) {
 5       NodeList nl = root.getChildNodes();
 6       for (int i = 0; i < nl.getLength(); i++) {
 7          Node node = nl.item(i);
 8          if (node instanceof Element) {
 9             Element ele = (Element) node;
10             // 解析預設名稱空間元素,如<bean>標籤
11             if (delegate.isDefaultNamespace(ele)) {
12                parseDefaultElement(ele, delegate);
13             }
14             // 解析自定義名稱空間元素,如<aop>相關標籤
15             else {
16                delegate.parseCustomElement(ele);
17             }
18          }
19       }
20    }
21    else {
22       delegate.parseCustomElement(root);
23    }
24 }
  parseBeanDefinitions()解析document物件文件樹中的預設標籤、自定義標籤。關於beanDefinition的具體解析,我們放在下一章節介紹。

1.2、初始化容器 - refreshBeanFactory()

  AbstractApplicationContext中的refreshBeanFactory方法由其子類ConfigurableListableBeanFactory實現,返回的是在refreshBeanFactory()方法中賦值的beanFactory。

2、總結

obtainFreshBeanFactory()核心流程總結

2.1、建立beanFactory容器

  建立容器DefaultListableBeanFactory物件,同時初始化容器中的1allowBeanDefinitionOverriding、allowCircularReferences等屬性。

2.2、loadBeanDefinition載入bean定義核心流程

·建立BeanDefinitionReader,bean定義讀取器物件,並將容器beanFactory回撥至讀取器中; ·為BeanDefinitionReader設定環境物件,資源解析器; ·建立document物件   從配置檔案路徑location陣列String[] -> Resource物件陣列 -> Resource物件封裝為InputStream輸入流 -> InputSource物件 -> document物件 ·建立documentReader物件; ·建立bean定義解析代理BeanDefinitionParserDelegate物件; ·解析document各文件節點,獲取beanDefinition並載入至容器中。 ·預設標籤解析 ·自定義標籤解析