SpringBoot原理解析(一)- 基于xml配置bean(Java解析xml文件)

SpringBoot原理解析(一)- 基于xml配置bean(Java解析xml文件)

文章目录

XML(eXtensible Markup Language)是一种标记语言,由SGML简化而来,‌是许多置标语言(‌例如XMLs/RDF/RDFs/OWL等)‌的元语言,用于描述和组织数据的结构。

在Spring项目中,XML(eXtensible Markup Language)被广泛用作配置文件格式,用于定义和配置Spring应用程序的各种组件和功能。通过XML配置,可以将Java对象声明为Spring管理的Bean,并指定其生命周期、依赖关系和其他属性。

本章不会解析spring源码,仅以spring的application.xml为例,将你如何利用读取xml文件。

1.DOM 和 SAX 介绍

1.1.DOM(Document Object Model)

DOM解析器将整个XML文档加载到内存中,并将其表示为树状结构。这意味着可以从根节点开始访问和操作文档中的任何部分,操作简单且灵活。

优点:
  1. 完整性和准确性:DOM模型在加载XML文档时校验文档的合法性,确保文档符合XML规范。
  2. 具有层次结构:DOM模型将XML文档表示为具有层次结构的树,这使得解析和操作复杂的文档结构变得容易。可以通过导航和遍历节点来访问和操作文档中的数据。
  3. 操作灵活,支持增删改查:DOM提供了一组标准的API方法,可以轻松地创建、修改、删除和查询XML文档的节点和属性。这使得开发者可以根据需要对文档进行灵活的操作和更新。
缺点:
  1. 内存占用高:由于DOM将整个XML文档加载到内存中,对于较大的XML文档,它可能消耗大量的内存。如果XML文档特别大,可能会引发内存溢出的问题。
  2. 性能较差:由于DOM需要加载整个文档到内存中,因此解析大型的XML文档会导致性能下降。而且,对文档进行频繁的修改和更新操作也会影响性能。
  3. 不适合流式处理:由于DOM需要将整个文档加载到内存中,因此它不适合处理大型、较长的XML文档或流式XML数据。相比之下,SAX(Simple API for XML)解析器更适合流式处理。
  4. API复杂:DOM提供了一组庞大的API方法和属性,需要开发者熟悉和理解不同的节点类型、方法和属性,以正确地操作和处理XML文档。

1.2.SAX(Simple API for XML)

SAX 解析器基于事件驱动的,逐行读取 XML 文档并触发事件。

优点:
  1. 内存占用低:SAX解析器以事件驱动的方式逐行读取XML文档,并在解析过程中生成事件。相比于DOM解析器将整个文档加载到内存中,SAX解析器逐行读取XML文档,减少了内存的占用。
  2. 高性能:由于SAX解析器是事件驱动的,它避免了将整个文档加载到内存中的开销,因此能够更高效地处理大型和长文档。对于只需要读取XML文档而不需要修改的场景,SAX解析器通常具有更好的性能。
  3. 适合流式处理:SAX解析器逐行读取XML文档,使得它非常适合处理大型、较长的XML文档或流式XML数据。它可以通过触发事件来逐行处理数据,而无需将整个文档加载到内存中。
  4. 简单易用:SAX提供了一组简单的API,使得它相对于DOM更易学和使用。开发者只需要实现事件处理器接口,并根据具体需求来处理读取到的事件即可。
缺点:
  1. 无法直接修改:SAX解析器是只读的,一旦读取到XML文档的某一部分,就无法直接修改它。如果需要对文档进行修改,需要使用其他方式(如DOM)来操作。
  2. 难以处理复杂结构:由于SAX是基于事件的,开发者需要逐行处理XML文档中的数据。对于复杂的文档结构,可能需要编写复杂的逻辑来处理各种事件。
  3. 缺乏上下文:SAX解析器只在解析过程中提供有限的上下文信息,对于需要在整个文档中进行一些上下文相关的操作,可能需要额外的处理。
  4. 需要自定义解析逻辑:SAX解析器是基于事件驱动的,开发者需要自定义解析逻辑来处理读取到的事件。相比于DOM解析器提供的一组标准的API,SAX可能需要额外的编码工作。

2.前置准备

2.1.applicaton.xml

application.xml配置文件内容如下

<?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="jack" name="jack" class="org.ahao.xml.test.pojo.People">
        <property name="name" value="Jack"/>
    </bean>
</beans>

2.2.Personl

public class People {
    
    

    private String name;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

}

2.3.BeanDefinitionElement

public class BeanDefinitionElement {
    
    

    private String id;

    private String name;

    private String clazz;

    /**
     * 属性集合
     */
    private List<BeanProperty> properties;

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getClazz() {
    
    
        return clazz;
    }

    public void setClazz(String clazz) {
    
    
        this.clazz = clazz;
    }

    public List<BeanProperty> getProperties() {
    
    
        return properties;
    }

    public void setProperties(List<BeanProperty> properties) {
    
    
        this.properties = properties;
    }

    @Override
    public String toString() {
    
    
        return "BeanDefinitionElement{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", clazz='" + clazz + '\'' +
                ", properties=" + properties +
                '}';
    }
}

2.4.BeanProperty

public class BeanProperty {
    
    

    private String name;
    private String value;
    private String ref;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getValue() {
    
    
        return value;
    }

    public void setValue(String value) {
    
    
        this.value = value;
    }

    public String getRef() {
    
    
        return ref;
    }

    public void setRef(String ref) {
    
    
        this.ref = ref;
    }

    @Override
    public String toString() {
    
    
        return "BeanProperty{" +
                "name='" + name + '\'' +
                ", value='" + value + '\'' +
                ", ref='" + ref + '\'' +
                '}';
    }
}

3.DOM实战

3.1.Document接口

org.w3c.dom.Document,表示整个XML文档,并提供了各种方法来操作XML文档的不同部分。以下是org.w3c.dom.Document接口的一些常用方法及其作用:

  1. getElementById(String id):根据给定的id值获取具有指定ID的元素节点。
  2. getElementsByTagName(String tagName):根据给定的标签名获取全部指定标签名的元素节点列表。
  3. getElementsByTagNameNS(String namespaceURI, String localName):根据给定的命名空间URI和本地名称获取全部指定标签名的元素节点列表。
  4. getDocumentElement():获取文档的根元素节点。
  5. createElement(String tagName):创建一个具有指定标签名的元素节点。
  6. createTextNode(String data):创建一个包含指定文本内容的文本节点。
  7. createAttribute(String name):创建一个具有指定名称的属性节点。
  8. createComment(String data):创建一个包含指定注释内容的注释节点。
  9. importNode(Node importedNode, boolean deep):将一个节点从另一个文档导入到当前文档,并返回导入的节点。
  10. appendChild(Node newChild):将指定的节点添加为当前文档的最后一个子节点。
  11. insertBefore(Node newChild, Node refChild):将指定的节点插入到给定参考节点之前的位置。
  12. removeChild(Node oldChild):从当前文档中移除指定的子节点。
  13. replaceChild(Node newChild, Node oldChild):将指定的新节点替换为当前文档中的给定旧节点。

3.2.Node接口

org.w3c.dom.Node接口,用于表示XML文档中的任何类型节点。以下是org.w3c.dom.Node接口的一些常用方法及其作用:

  1. getNodeName():获取节点的名称。
  2. getNodeValue():获取节点的值,对于元素节点和文本节点,返回null。
  3. getParentNode():获取节点的父节点。
  4. getChildNodes():获取节点的所有子节点的列表。
  5. getFirstChild():获取节点的第一个子节点。
  6. getLastChild():获取节点的最后一个子节点。
  7. getNextSibling():获取节点的下一个兄弟节点。
  8. getPreviousSibling():获取节点的上一个兄弟节点。
  9. getNodeType():获取节点的类型,以整数形式返回,例如1表示元素节点,3表示文本节点,等等。
  10. getTextContent():获取节点及其所有后代节点的文本内容。
  11. hasAttributes():检查节点是否具有属性。
  12. hasChildNodes():检查节点是否有子节点。
  13. setNodeValue(String nodeValue):设置节点的值。
  14. appendChild(Node newChild):将指定的节点添加为当前节点的最后一个子节点。
  15. insertBefore(Node newChild, Node refChild):将指定的节点插入到给定参考节点之前的位置。
  16. removeChild(Node oldChild):删除当前节点的指定子节点。

3.3.Demo演示

public class DomTest {
    
    

    public static void main(String[] args) {
    
    
        List<BeanDefinitionElement> beanDefinitions = new ArrayList<>();

        // 获取类路径下application.xml配置文件的绝对路径
        String pathname = ResourceUtil.toAbsolutePath("application.xml");
        // 1.创建DOM解析器工厂
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        try {
    
    
            // 2.创建DOM解析器
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            // 3.读取XML文件并解析为Document对象
            Document parse = documentBuilder.parse(new File(pathname));
            // 4.获取根节点/元素
            Element root = parse.getDocumentElement();
            System.out.println("根元素:"+root);
            // 5.获取根元素的子节点
            NodeList childNodes = root.getChildNodes();
            if (childNodes != null){
    
    
                // 6.遍历根元素的子节点
                for (int i = 0; i < childNodes.getLength(); i++) {
    
    
                    Node item = childNodes.item(i);
                    if ("bean".equals(item.getNodeName())){
    
    
                        // 创建BeanDefinitionElement
                        BeanDefinitionElement beanDefinitionElement = new BeanDefinitionElement();
                        beanDefinitions.add(beanDefinitionElement);
                        // 判断是否具有属性
                        if (item.hasAttributes()){
    
    
                            // 获取bean标签的属性集合
                            NamedNodeMap attributes = item.getAttributes();
                            // 获取name属性
                            Node name = attributes.getNamedItem("name");
                            beanDefinitionElement.setName(name.getNodeValue());
                            // 获取id属性
                            Node id = attributes.getNamedItem("id");
                            beanDefinitionElement.setId(id.getNodeValue());
                            // 获取class属性
                            Node clazz = attributes.getNamedItem("class");
                            beanDefinitionElement.setClazz(clazz.getNodeValue());
                        }
                        // 获取bean标签的子节点列表
                        NodeList beanChildNodes = item.getChildNodes();
                        if (beanChildNodes != null){
    
    
                            List<BeanProperty> properties = new ArrayList<>();
                            for (int j = 0; j < beanChildNodes.getLength(); j++) {
    
    
                                Node property = beanChildNodes.item(i);
                                if ("property".equals(property.getNodeName())){
    
    
                                    // 创建BeanProperty
                                    BeanProperty beanProperty = new BeanProperty();
                                    properties.add(beanProperty);
                                    if (property.hasAttributes()) {
    
    
                                        // 获取property标签的属性集合
                                        NamedNodeMap attributes = property.getAttributes();
                                        Node name = attributes.getNamedItem("name");
                                        beanProperty.setName(name.getNodeValue());
                                        Node value = attributes.getNamedItem("value");
                                        beanProperty.setValue(value.getNodeValue());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (ParserConfigurationException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        } catch (SAXException e) {
    
    
            throw new RuntimeException(e);
        }
        System.out.println(beanDefinitions);
    }

}
// 控制台输出
根元素:[beans: null]
[BeanDefinitionElement{
    
    id='jack', name='jack', clazz='org.ahao.xml.test.pojo.People', properties=null}]

4.SAX实战

4.1.DefaultHandler接口

org.xml.sax.helpers.DefaultHandler是SAX解析器的默认事件处理程序类。以下是org.xml.sax.helpers.DefaultHandle接口的一些常用方法及其作用:

  1. startDocument():在解析开始时调用,标志着文档的开始。
  2. endDocument():在解析结束时调用,标志着文档的结束。
  3. startElement(String uri, String localName, String qName, Attributes attributes):开始解析一个元素时调用。
    • uri: 元素的命名空间URI。
    • localName: 元素的本地名称。
    • qName: 元素的限定名称。
    • attributes: 元素的属性列表。
  4. endElement(String uri, String localName, String qName):解析一个元素的结束标签时调用。
  5. characters(char[] ch, int start, int length):解析元素内容时调用。
    • ch: 字符数组,包含元素文本内容。
    • start: 字符数组的起始位置。
    • length: 字符数组中的有效字符长度。
  6. startPrefixMapping(String prefix, String uri):处理元素的命名空间映射时调用。
  7. endPrefixMapping(String prefix):处理元素的命名空间映射结束时调用。

4.2.BeanParserHandler

自定义的解析事件处理器

public class BeanParserHandler extends DefaultHandler {
    
    

    /**
     * 存储bean标签元素对象
     */
    private List<BeanDefinitionElement> beanDefinitions = new ArrayList<>();

    /**
     * bean内部 property元素标签
     */
    private List<BeanProperty> properties;

    // 是否处于bean元素标签中
    private boolean inBeanElement = false;

    // 解析元素的开始标签
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    
    
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals("bean")){
    
    
            BeanDefinitionElement curBeanElement = new BeanDefinitionElement();
            // 获取id属性
            String id = attributes.getValue("id");
            curBeanElement.setId(id);
            // 获取name属性
            String name = attributes.getValue("name");
            curBeanElement.setName(name);
            // 获取class属性
            String clazz = attributes.getValue("class");
            curBeanElement.setClazz(clazz);
            beanDefinitions.add(curBeanElement);
            properties = new ArrayList<>();
            curBeanElement.setProperties(properties);
            inBeanElement = true;
        }else if ("property".equals(qName)){
    
    
            BeanProperty beanProperty = new BeanProperty();
            // 获取name属性
            String name = attributes.getValue("name");
            beanProperty.setName(name);
            // 获取value属性
            String value = attributes.getValue("value");
            beanProperty.setValue(value);
            // 获取ref属性
            String ref = attributes.getValue("ref");
            beanProperty.setRef(ref);
            properties.add(beanProperty);
        }
    }

    // 解析元素的结束标签
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
    
    
        super.endElement(uri, localName, qName);
        if (inBeanElement && "bean".equals(qName)){
    
    
            inBeanElement = false;
            properties = new ArrayList<>();
        }
    }

    // 获取BeanDefinitionElement列表
    public List<BeanDefinitionElement> getBeanDefinitions() {
    
    
        return beanDefinitions;
    }

}

4.3.Demo演示

public class SAXTest {
    
    

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
    
    
        // 1.获取文件绝对路径
        String s = ResourceUtil.toAbsolutePath("application.xml");
        // 2.创建SAX解析器工厂
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        // 3.创建SAX解析器实例
        SAXParser saxParser = saxParserFactory.newSAXParser();
        // 4.创建自定义的解析事件处理器
        BeanParserHandler beanParserHandler = new BeanParserHandler();
        // 5.解析XML文件
        saxParser.parse(s,beanParserHandler);
        // 获取解析后的BeanDefinitionElement列表
        List<BeanDefinitionElement> beanDefinitions = beanParserHandler.getBeanDefinitions();
        System.out.println(beanDefinitions);
    }

}
// 控制台输出
[BeanDefinitionElement{
    
    id='jack', name='jack', clazz='org.ahao.xml.test.pojo.People', properties=[BeanProperty{
    
    name='name', value='Jack', ref='null'}]}]

5.Spring解析XML文件

以下为ClassPathXmlApplicationContext的启动流程中,读取Resource中有关BeanDefinition的加载过程

spring-boot:2.7.14 或者 spring-context:5.3.29

5.1.org.springframework.context.support.AbstractApplicationContext#refresh

public void refresh() throws BeansException, IllegalStateException {
    
    
        synchronized(this.startupShutdownMonitor) {
    
    
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
          	// this.obtainFreshBeanFactory()
          	// 创建一个新的DefaultListableBeanFactory实例作为BeanFactory,并进行必要的初始化和配置
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
    
    
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var10) {
    
    
                if (this.logger.isWarnEnabled()) {
    
    
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }

                this.destroyBeans();
                this.cancelRefresh(var10);
                throw var10;
            } finally {
    
    
                this.resetCommonCaches();
                contextRefresh.end();
            }

        }
    }

5.2.org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

		protected final void refreshBeanFactory() throws BeansException {
    
    
        if (this.hasBeanFactory()) {
    
    
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
    
    
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
          	// 加载BeanDefinition
            this.loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        } catch (IOException var2) {
    
    
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
        }
    }

5.3.org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    
    
        // XmlBeanDefinitionReader: 用于从XML配置文件中读取Bean的定义信息
      	// 在Spring框架中,Bean的定义信息通常是通过XML文件进行配置的。XmlBeanDefinitionReader类提供了一些方法,用于解析和读取XML文件中的Bean定义信息,并将其转换为Spring框架中的内部数据结构。
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
      	// 加载BeanDefinition
        this.loadBeanDefinitions(beanDefinitionReader);
    }

5.4.org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    
    
        // 用于获取配置文件的资源数组
      	Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
    
    
            // 加载BeanDefinition(到了此处,才算开始解析)
            reader.loadBeanDefinitions(configResources);
        }
				// 获取配置文件位置(字符串)
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
    
    
            reader.loadBeanDefinitions(configLocations);
        }

    }

5.5.org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource…)

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    
    
    Assert.notNull(resources, "Resource array must not be null");
    int count = 0;
    Resource[] var3 = resources;
    int var4 = resources.length;

    for(int var5 = 0; var5 < var4; ++var5) {
    
    
        Resource resource = var3[var5];
        // 加载BeanDefinition
        count += this.loadBeanDefinitions((Resource)resource);
    }

    return count;
}

5.6.org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    
    
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (this.logger.isTraceEnabled()) {
    
    
        this.logger.trace("Loading XML bean definitions from " + encodedResource);
    }

    Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
    if (!currentResources.add(encodedResource)) {
    
    
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    } else {
    
    
        int var6;
        try {
    
    
            InputStream inputStream = encodedResource.getResource().getInputStream();
            Throwable var4 = null;

            try {
    
    
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
    
    
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
								// 执行从XML配置文件加载BeanDefinition的方法
                var6 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } catch (Throwable var24) {
    
    
                var4 = var24;
                throw var24;
            } finally {
    
    
                if (inputStream != null) {
    
    
                    if (var4 != null) {
    
    
                        try {
    
    
                            inputStream.close();
                        } catch (Throwable var23) {
    
    
                            var4.addSuppressed(var23);
                        }
                    } else {
    
    
                        inputStream.close();
                    }
                }

            }
        } catch (IOException var26) {
    
    
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var26);
        } finally {
    
    
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
    
    
                this.resourcesCurrentlyBeingLoaded.remove();
            }

        }

        return var6;
    }
}

5.7.org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    
    
        try {
    
    
          	// 此处的doc类型,就是上面DOM实战的org.w3c.dom.Document接口
          	// 就是解析XML配置文件为Document对象
            Document doc = this.doLoadDocument(inputSource, resource);
          	// 
            int count = this.registerBeanDefinitions(doc, resource);
            if (this.logger.isDebugEnabled()) {
    
    
                this.logger.debug("Loaded " + count + " bean definitions from " + resource);
            }

            return count;
        } catch (BeanDefinitionStoreException var5) {
    
    
            throw var5;
        } catch (SAXParseException var6) {
    
    
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
        } catch (SAXException var7) {
    
    
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
        } catch (ParserConfigurationException var8) {
    
    
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
        } catch (IOException var9) {
    
    
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
        } catch (Throwable var10) {
    
    
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
        }
    }

5.8.org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    
    
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 解析doc注册BeanDefinition
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

5.9.org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    
    
   this.readerContext = readerContext;
   // 解析并注册BeanDefinition
   doRegisterBeanDefinitions(doc.getDocumentElement());
}

5.10.org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {
    
    
   // Any nested <beans> elements will cause recursion in this method. In
   // order to propagate and preserve <beans> default-* attributes correctly,
   // keep track of the current (parent) delegate, which may be null. Create
   // the new (child) delegate with a reference to the parent for fallback purposes,
   // then ultimately reset this.delegate back to its original (parent) reference.
   // this behavior emulates a stack of delegates without actually necessitating one.
   BeanDefinitionParserDelegate parent = this.delegate;
   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;
         }
      }
   }

   preProcessXml(root);
   // 解析并注册BeanDefinition
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root);

   this.delegate = parent;
}

5.11.org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

	public static final String NESTED_BEANS_ELEMENT = "beans";

	// public static final String BEAN_ELEMENT = "bean";
	public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;

	public static final String ALIAS_ATTRIBUTE = "alias";

	public static final String IMPORT_ELEMENT = "import";

	// 此方法从配置文件根元素开始,逐个解析子元素。
	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)) {
    
    
	               // 解析元素,并转换为Spring内部的数据结构然后注册到BeanFactory中
	               parseDefaultElement(ele, delegate);
	            }
	            else {
    
    
	               delegate.parseCustomElement(ele);
	            }
	         }
	      }
	   }
	   else {
    
    
	      delegate.parseCustomElement(root);
	   }
	}

  // 在该方法中,Spring会根据XML配置文件中的默认元素节点(比如<bean>,<alias>)来解析和创建对应的Bean定义。
  // 该方法会根据元素节点的不同类型和属性进行解析和处理,如根据<bean>节点的属性来创建Bean实例,<alias>节点来为Bean定义添加别名。
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    
    
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
    
    
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
    
    
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    
    
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    
    
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

6.总结

本篇博文讲解java中内置的两种解析XML文档的API,并分别演示使用方式。最后简单的过了一下Spring中关于解析XML配置文件,并注册BeanDefinition的相关源码。其实博主所演示的DOM案例,和Spring中最后解析注册BeanDefinition的原理是一样的,只不过Spring配置文件更加复杂,所以如果想深入了解具体的解析过程还需读者自己研究(其实这一部分内容并不重要,只需知道有这个过程即可)。

猜你喜欢

转载自blog.csdn.net/qq_51513626/article/details/140604500