首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring IOC 容器啟動流程深度解析:從 XML 解析到 Bean 獲取全過程

     編輯:Java 相關知識

引言:簡單 Demo 背後的完整鏈路

以經典 XML 配置為例,啟動流程僅四行代碼,卻涵蓋 Spring IOC 核心生命週期:

Resource resource = new ClassPathResource("spring-core.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
MyBean myBean = (MyBean) beanFactory.getBean("myBean");
myBean.sayHello();

本文將從Bean 解析註冊與Bean 獲取實例化兩大階段,梳理核心源碼邏輯。

階段一:Bean 定義解析與註冊

1. XmlBeanFactory 建構過程

public XmlBeanFactory(Resource resource) throws BeansException {
    this(resource, null);
}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    super(parentBeanFactory);  // 初始化 DefaultListableBeanFactory
    this.reader.loadBeanDefinitions(resource);  // 觸發解析
}

XmlBeanFactory 繼承 DefaultListableBeanFactory(核心 IOC 容器實現),內持有 XmlBeanDefinitionReader 負責 XML 解析。

2. loadBeanDefinitions 核心流程

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    // 1. 防循環引用檢查
    // 2. 獲取 InputStream → 構造 InputSource
    // 3. 調用 doLoadBeanDefinitions(inputSource, resource)
}

3. 文件解析三步曲

  1. 獲取驗證模式:DTD/XSD 自動檢測,支援 VALIDATION_AUTO、VALIDATION_XSD、VALIDATION_DTD
  2. 實體解析器:解決網絡不可用時無法下載 XSD 的問題,Spring 內建映射本地 XSD
  3. DocumentBuilder 解析:JAXP 標準流程獲取 org.w3c.dom.Document

4. Document 解析並註冊 BeanDefinition

public int registerBeanDefinitions(Document doc, Resource resource) {
    BeanDefinitionDocumentReader reader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    reader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

實際工作由 DefaultBeanDefinitionDocumentReader 完成:

protected void doRegisterBeanDefinitions(Element root) {
    // 1. 處理 profile 屬性(環境隔離)
    // 2. preProcessXml(root) - 模板方法預留擴展
    // 3. parseBeanDefinitions(root, delegate) - 核心解析
    // 4. postProcessXml(root) - 模板方法預留擴展
}

5. 標籤解析分派

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        // 默認命名空間:, ,  等
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);  // 解析  等
                } else {
                    delegate.parseCustomElement(ele);    // 解析自定義標籤
                }
            }
        }
    } else {
        delegate.parseCustomElement(root);  // 非默認命名空間直接委派
    }
}

至此,XML 靜態配置轉化為內存中的 BeanDefinition 元數據,註冊至 DefaultListableBeanFactory.beanDefinitionMap。

階段二:Bean 獲取與實例化

1. getBean 入口

public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

2. doGetBean 核心邏輯(簡化版)

protected  T doGetBean(String name, Class requiredType, Object[] args, boolean typeCheckOnly) {
    // 1. 轉換 beanName(處理 alias、FactoryBean 前綴 &)
    final String beanName = transformedBeanName(name);

    // 2. 檢查單例緩存(含早期暴露的 ObjectFactory,解決循環依賴)
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        return getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    // 3. 原型模式循環依賴檢查
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }

    // 4. 父子容器委派查找
    BeanFactory parent = getParentBeanFactory();
    if (parent != null && !containsBeanDefinition(beanName)) {
        return parent.getBean(nameToLookup, args);
    }

    // 5. 合併 BeanDefinition(處理父子定義繼承)
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    checkMergedBeanDefinition(mbd, beanName, args);

    // 6. 處理 depends-on 依賴
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
        for (String dep : dependsOn) {
            if (isDependent(beanName, dep)) throw new BeanCreationException(...);
            registerDependentBean(dep, beanName);
            getBean(dep);  // 遞歸初始化依賴
        }
    }

    // 7. 根據 scope 創建實例
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> createBean(beanName, mbd, args));
        return getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    } else if (mbd.isPrototype()) {
        Object prototypeInstance = createBean(beanName, mbd, args);
        return getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    } else {
        Scope scope = scopes.get(mbd.getScope());
        Object scopedInstance = scope.get(beanName, () -> createBean(beanName, mbd, args));
        return getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
}

3. 關鍵機制深度剖析

單例模式與循環依賴解決

  • 早期暴露:getSingleton(beanName, ObjectFactory) 中,實例創建前先將 ObjectFactory 放入 singletonFactories 三級緩存
  • 三級緩存:
    1. singletonObjects:完成初始化的單例
    2. earlySingletonObjects:早期暴露的單例(已實例化,未完成屬性注入)
    3. singletonFactories:ObjectFactory 延遲創建
  • 流程:A 創建中需 B → B 創建中需 A → A 從三級緩存獲取早期引用 → 完成注入 → 移至一級緩存

FactoryBean 處理

getObjectForBeanInstance 判斷實例是否為 FactoryBean,若是則調用 getObject() 獲取真實物件,&beanName 可獲取 FactoryBean 本身。

類型轉換

若 requiredType 不匹配,嘗試 TypeConverter.convertIfNecessary 轉換。

核心類別關係圖

DefaultListableBeanFactory
    ├── BeanDefinitionRegistry (註冊 BeanDefinition)
    ├── BeanFactory (getBean 入口)
    ├── AliasRegistry (別名管理)
    ├── SingletonBeanRegistry (單例管理)
    └── ConfigurableBeanFactory (配置能力)

總結:從 XML 到可用 Bean 的完整鏈路

  1. Resource 載入:ClassPathResource 封裝定位資源
  2. XML 解析:XmlBeanDefinitionReader → Document → BeanDefinitionDocumentReader
  3. 註冊:BeanDefinition 存入 beanDefinitionMap
  4. 獲取觸發創建:getBean → doGetBean
  5. 依賴解析:depends-on、構造器/Setter 注入遞歸
  6. 實例化:createBean → 實例化 → 屬性填充 → 初始化(Aware、InitializingBean、@PostConstruct)
  7. 後處理:BeanPostProcessor 前後置增強(AOP 代理在此生成)
  8. 註冊銷毀:單例註冊 DestructionAwareBeanPostProcessor
  9. 返回:getObjectForBeanInstance 處理 FactoryBean、類型轉換

理解此流程,是掌握 Spring 核心、排查啟動異常、實現擴展點的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved