Spring IOC 源碼解析(一),容器中的 BeanDefinition(XML 解析篇)

前言

Spring 的核心就是 Bean,圍繞這 Bean 這個概念衍生出來 IOC(控制反轉),AOP(面向切面編程),該系列文章主要分析 IOC 源碼;php

IOC(控制反轉):意思就是將咱們日常編程中人爲建立對象和管理對象的這一系列複雜關係,交給 Spring 容器去作html

測試代碼

@Test
    public void t5() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring_1.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.queryUser("long");
    }
複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userService" class="com.example.ana.service.impl.UserServiceImpl" />
</beans>
複製代碼

找到 BeanDefinition 的加載入口

  1. 調用 setConfigLocations(String ...location) 將傳入的 "classpath:spring_1.xml" 保存到 configLocations 中
  2. 而後調用 refresh() 後依次調用到 AbstractXmlApplicationContext 的 loadBeanDefinitions(beanFactory) 方法,方法參數的 BeanFactory 會在上一次調用中建立好,後續解析到的 BeanDefinition 就可以正確的放入該容器中

根據配置文件路徑將其解析爲 Resource 和 Document

  1. 根據以前保存的 configLocations 經過 ResourcePatternResolver(ResourceLoader 的子類) 進行解析返回 Resource[],覺得這裏咱們只有一個配置文件 spring_1.xml 因此返回數組只有一個值,Resource 表明一個資源,它描述了一個文件的相關信息 java

  2. 根據 Resource 獲取其 InputStream,其實就是 resource.getFile().getInputstream(),而後根據 InputStream 建立 InputSourcenode

  3. 而後調用 DefaultDocumentLoader 的 loadDocument(InputSource inputSource, EntityResolver entityResolver, ...) 方法spring

  4. 建立 DocumentBuilder編程

  5. 調用 parse(InputSource is)數組

  6. 最終調用 DomParser 的 parse(InputSource inputSource) 方法,將其解析爲 Documentapp

其實總結下來就一句話,根據 configLocations 對應的文件解析爲 Resource,而後經過 Resource 獲取到文件輸入流,最後經過 DomParser 將輸入流進行解析爲 Document測試

Document 是文檔的根節點,他的實現類,包含了 xml 或者 html 等文檔各個節點和命名空間的數據,好比說它其中的 Element 的 node 屬性就能包含 <bean id="xxx" class="xxx" /> 這樣一個節點數據,可以獲取到它的 id、class 等,經過這個 Document 的信息就能比較方便的建立 BeanDefinition 了ui

將 Document 解析爲 BeanDefinition 並放入容器

  1. 首先建立 DefaultBeanDefinitionDocumentReader 經過它來讀取 Document 將其解析爲 BeanDefinition
  2. 建立 XmlReaderContext,用它來保存讀取解析上下文的信息
  3. 建立一個 BeanDefinitionParserDelegate 委派器
  4. 而後經過 Document 獲取其 Element,經過 Element 獲取全部的 node(也是一種 Element) 結點進行遍歷,若是發現是 http://www.springframework.org/schema/beans 這樣的命名空間,那麼就 BeanDefinitionParserDelegate 這個委派器解析 Element 元素將其解析後返回 BeanDefinitionHolder,解析流程不復雜大致就是經過 Element 獲取 bean 元素的 id,class,別名等信息來建立 BeanDefinitionHolder,new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray)
  5. 調用 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  6. 最後調用 getReaderContext().getRegistry().registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法,實際上調用的 DefaultListableBeanFactory 的 registerBeanDefinition 方法,將其放入容器中
// 主要邏輯
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
    this.beanDefinitionMap.put(beanName, beanDefinition);
}
複製代碼
  1. 而後循環第 4 步的後續節點,依次處理完,最後文檔定義的 bean 等元素所有解析爲 BeanDefinition 放入 BeanFactory 容器中了

總結

spring 首先將配置文件解析爲 Resource,而後根據 Resouce 獲取其輸入流信息解析生成 Document 經過該 Document 獲取到的 Element 包含了定義的全部節點信息,而後經過 Element 獲取全部的 Bean 結點遍歷依次解析,解析就是經過這些結點獲取 bean 定義的 id、class、alias 等信息,根據這些信息來建立 BeanDefinition,最後將 BeanDefinition 放入 DefaultListableBeanFactory 的 BeanDefinitionMap 中同時保存一份 beanDefinitionNames

相關文章
相關標籤/搜索