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

Spring XmlBeanFactory 源碼導讀:從 Resource 定位到 BeanDefinition 註冊的完整解析鏈路

     編輯:Java 相關知識

XmlBeanFactory 定位:Spring 最基礎的 XML 驅動 IOC 容器

XmlBeanFactory 繼承 DefaultListableBeanFactory,是 Spring 早期提供的輕量級容器實現(現已標記 @Deprecated,建議使用 ApplicationContext)。其源碼極簡,卻清晰展示了 資源定位 → XML 解析 → BeanDefinition 註冊 的核心鏈路,是理解 Spring 容器啟動本質的最佳切入點。

類結構與構造函數

public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }
    
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory); // 設置父 BeanFactory,支援層級查找
        this.reader.loadBeanDefinitions(resource); // 核心:載入 Bean 定義
    }
}

關鍵點:

  • 內部持有 XmlBeanDefinitionReader,構造時注入 this(作為 BeanDefinitionRegistry)。
  • 支持父子工廠鏈:parentBeanFactory 向上委派 getBean() 查找。
  • 構造函數直接觸發載入,無延遲加載(區別於 ApplicationContext 的 refresh())。

資源抽象:Resource 接口體系

Resource resource 參數統一了配置來源:

所有實現均提供 getInputStream()、exists()、getFilename()、getDescription() 等統一訪問接口。

XmlBeanDefinitionReader.loadBeanDefinitions() 解析入口

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource)); // 包裝編碼信息
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    InputStream inputStream = encodedResource.getResource().getInputStream();
    try {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) inputSource.setEncoding(encodedResource.getEncoding());
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    } finally {
        inputStream.close();
    }
}

職責:資源 → InputStream → InputSource(SAX/DOM 通用輸入源),處理編碼,委託 doLoadBeanDefinitions()。

doLoadBeanDefinitions():DOM 解析與註冊分發

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
        int validationMode = getValidationModeForResource(resource); // DTD/XSD 驗證模式
        Document doc = documentLoader.loadDocument(inputSource, entityResolver, errorHandler, validationMode, namespaceAware);
        return registerBeanDefinitions(doc, resource); // 委託註冊
    } catch (Exception ex) {
        throw new BeanDefinitionStoreException("Failed to load bean definitions from " + resource, ex);
    }
}

關鍵組件:

  • DefaultDocumentLoader:使用 JAXP DocumentBuilderFactory 解析 XML 為 DOM Document。
  • ResourceEntityResolver:解析 Spring DTD/XSD(支援 classpath: 协议)。
  • registerBeanDefinitions(Document, Resource):分發到具體解析器。

registerBeanDefinitions():解析器策略模式

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 老版本支援自定義 parserClass(默認 DefaultXmlBeanDefinitionParser)
    if (this.parserClass != null) {
        XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
        return parser.registerBeanDefinitions(this, doc, resource);
    }
    
    // 現代版本:BeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getBeanFactory().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getBeanFactory().getBeanDefinitionCount() - countBefore;
}

演進:早期 XmlBeanDefinitionParser 接口 → 現代 BeanDefinitionDocumentReader(默認 DefaultBeanDefinitionDocumentReader)。

DefaultBeanDefinitionDocumentReader:遞歸解析 元素

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    Element root = doc.getDocumentElement();
    BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
    preProcessXml(root);
    parseBeanDefinitions(root, delegate); // 核心遞歸入口
    postProcessXml(root);
}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root.getNamespaceURI())) {
        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.getNamespaceURI())) {
                    parseDefaultElement(ele, delegate); // 默認命名空間:、、
                } else {
                    delegate.parseCustomElement(ele); // 擴展命名空間:、、
                }
            }
        }
    } else {
        delegate.parseCustomElement(root);
    }
}

parseDefaultElement():三大核心元素處理

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 
        importBeanDefinitionResource(ele);
    } else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 
        String name = ele.getAttribute(NAME_ATTRIBUTE);
        String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
        getReaderContext().getReader().getBeanFactory().registerAlias(name, alias);
        getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
    } else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { // 
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 處理 、 等
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
}

BeanDefinitionParserDelegate.parseBeanDefinitionElement(): 完整解析

解析屬性並構建 GenericBeanDefinition(或 RootBeanDefinition/ChildBeanDefinition):

  • 標識:id、name(別名)、alias。
  • 類信息:class、parent(繼承 BeanDefinition)。
  • 作用域:scope(singleton/prototype/request/session/自定義)。
  • 懶加載:lazy-init。
  • 依賴注入模式:autowire(no/byName/byType/constructor)、autowire-candidate。
  • 依賴關係:depends-on(初始化順序)、factory-bean/factory-method(工廠方法)。
  • 構造函數參數:<constructor-arg>(索引、類型、名稱、值/引用)。
  • 屬性注入:<property>(名稱、值/引用/列表/Map/Set/Props)。
  • 生命週期:init-method、destroy-method。
  • 方法覆蓋/查找:<lookup-method>、<replaced-method>(方法注入)。
  • 元數據:<meta>、<qualifier>(自動裝配限定符)。

解析完成返回 BeanDefinitionHolder(beanName + BeanDefinition + aliases)。

BeanDefinitionReaderUtils.registerBeanDefinition():註冊到註冊表

public static void registerBeanDefinition(BeanDefinitionHolder bdHolder, BeanDefinitionRegistry registry) {
    String beanName = bdHolder.getBeanName();
    registry.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());
    String[] aliases = bdHolder.getAliases();
    if (aliases != null) for (String alias : aliases) registry.registerAlias(beanName, alias);
}

DefaultListableBeanFactory.registerBeanDefinition():Map 存儲與驗證

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
    // 1. 驗證 BeanDefinition
    validateBeanDefinition(beanName, beanDefinition);
    
    // 2. 處理覆蓋
    BeanDefinition existing = beanDefinitionMap.get(beanName);
    if (existing != null) {
        if (!allowBeanDefinitionOverriding) throw new BeanDefinitionOverrideException(beanName);
        if (existing.getRole() < beanDefinition.getRole()) return; // 框架 Bean 優先
    } else {
        beanDefinitionNames.add(beanName); // 記錄註冊順序
    }
    
    // 3. 存入 Map
    beanDefinitionMap.put(beanName, beanDefinition);
    
    // 4. 清理單例緩存(若已提前實例化)
    removeSingleton(beanName);
}

完整調用鏈總結(文本版)

new XmlBeanFactory(resource)
    ↓
super(parentBeanFactory) → DefaultListableBeanFactory 初始化
    ↓
reader.loadBeanDefinitions(resource)
    ├─ loadBeanDefinitions(EncodedResource)
    │   ├─ resource.getInputStream() → InputStream
    │   ├─ new InputSource(inputStream) → InputSource
    │   └─ doLoadBeanDefinitions(inputSource, resource)
    │       ├─ documentLoader.loadDocument() → DOM Document
    │       └─ registerBeanDefinitions(doc, resource)
    │           ├─ createBeanDefinitionDocumentReader() → DefaultBeanDefinitionDocumentReader
    │           ├─ documentReader.registerBeanDefinitions(doc, readerContext)
    │           │   ├─ parseBeanDefinitions(root, delegate)
    │           │   │   ├─ parseDefaultElement(ele, delegate)
    │           │   │   │   ├─  → importBeanDefinitionResource() → 遞歸載入
    │           │   │   │   ├─  → registerAlias()
    │           │   │   │   └─  → delegate.parseBeanDefinitionElement() → BeanDefinitionHolder
    │           │   │   │       ├─ 解析所有屬性、子元素
    │           │   │   │       ├─ decorateBeanDefinitionIfRequired() → 處理 qualifier/meta
    │           │   │   │       └─ BeanDefinitionReaderUtils.registerBeanDefinition()
    │           │   │   │           └─ registry.registerBeanDefinition() → beanDefinitionMap.put()
    │           │   │   └─ 自定義命名空間 → delegate.parseCustomElement()
    │           │   └─ 返回註冊數量
    │           └─ 返回註冊數量
    └─ 返回註冊數量

XmlBeanFactory vs ApplicationContext 核心差異

實現類適用場景構造示例
ClassPathResource類路徑資源new ClassPathResource("beans.xml")
FileSystemResource文件系統絕對/相對路徑new FileSystemResource("/etc/config/beans.xml")
UrlResourceHTTP/FTP/JAR URLnew UrlResource("https://example.com/beans.xml")
InputStreamResource輸入流包裝new InputStreamResource(inputStream)
ByteArrayResource字節數組new ByteArrayResource(bytes)

現代替代方案:GenericApplicationContext + XmlBeanDefinitionReader

GenericApplicationContext context = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions("classpath:beans.xml");
context.refresh(); // 觸發完整初始化
// 使用 context.getBean(...)

這提供了 XmlBeanFactory 的簡潔性,同時擁有 ApplicationContext 的完整企業級功能。

總結

XmlBeanFactory 雖輕量,但完整展示了 Spring 容器啟動的 三大核心階段:

  1. 資源定位:Resource 抽象統一訪問類路徑、文件系統、網絡等配置源。
  2. 元數據解析:XmlBeanDefinitionReader → DocumentLoader → BeanDefinitionDocumentReader → BeanDefinitionParserDelegate,將 XML 轉為結構化 BeanDefinition。
  3. 註冊表構建:BeanDefinitionReaderUtils → DefaultListableBeanFactory.beanDefinitionMap,完成 Bean 定義的集中管理。

理解這條鏈路,配合 BeanDefinition、BeanDefinitionRegistry、ResourceLoader 等核心接口,你就掌握了 Spring IOC 容器「從配置到元數據」的轉化本質——這正是所有高級特性(AOP、事務、MVC、Batch 等)得以建立的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性XmlBeanFactoryApplicationContext
BeanDefinition 載入時機構造函數中立即載入refresh() 階段載入
單例預實例化否(getBean 時創建)是(finishBeanFactoryInitialization)
國際化不支持MessageSource
資源加載僅構造時指定的 ResourceResourceLoader 接口,支援多路徑、通配符
事件發布不支持ApplicationEventPublisher
生命週期回調不支持支援 @PostConstruct、@PreDestroy、InitializingBean、DisposableBean
Web 整合不支持WebApplicationContext、ServletContext 感知
父子上下文僅構造函數傳入 parentBeanFactory標準化父子上下文體系、遞歸查找