MyBatis 源码解析:XMLConfigBuilder 解析过程

前言

在 MyBatis 框架中,XMLConfigBuilder 是一个关键类,负责解析 mybatis-config.xml 配置文件。理解这个类的工作原理有助于掌握 MyBatis 的配置管理和框架初始化过程。本篇文章将通过自定义实现一个简化版的 XML 配置解析器,并深入解析 MyBatis 中 XMLConfigBuilder 的实现细节。


自定义实现:简化版 XML 配置解析器

目标与功能

我们将实现一个简化版的 XML 配置解析器,主要功能是解析 节点,并生成一个基本的配置对象。这个实现将帮助我们理解 MyBatis 的配置解析过程,为后续的源码解读打下基础。

XML 样例

在开始实现之前,我们先来看一个典型的 mybatis-config.xml 文件的片段:


DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    properties>

    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="false"/>
    settings>
configuration>
  • 节点:加载外部的属性文件或定义属性。
  • 节点:定义 MyBatis 的全局设置,如缓存启用和懒加载等。

实现过程

1. 定义解析器类

首先,我们创建一个名为 SimpleXMLConfigParser 的类,用于处理 XML 文件的解析。类中定义了两个 Map,分别用于存储 节点的内容。

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class SimpleXMLConfigParser {
    private Map<String, String> properties = new HashMap<>();  // 存储  节点内容
    private Map<String, String> settings = new HashMap<>();    // 存储  节点内容
2. 加载与解析 XML 文件

我们编写 parse 方法,该方法负责加载 XML 文件并依次解析 节点。

    public void parse(String filePath) throws Exception {
        // 创建解析器工厂和解析器
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new File(filePath));  // 解析 XML 文件

        // 解析  节点
        parseProperties(document);
        // 解析  节点
        parseSettings(document);
        // 校验配置的有效性
        validateConfiguration();
    }
  • DocumentBuilderFactoryDocumentBuilder:用于创建 XML 文档解析器。
  • Document:表示整个 XML 文件内容的对象,供我们解析和操作。
3. 解析 节点

接下来,我们编写 parseProperties 方法来解析 节点,并将解析结果存储在 properties Map 中。

    private void parseProperties(Document document) {
        NodeList propertyNodes = document.getElementsByTagName("property");
        for (int i = 0; i < propertyNodes.getLength(); i++) {
            Element property = (Element) propertyNodes.item(i);
            properties.put(property.getAttribute("name"), property.getAttribute("value"));
        }
    }
  • NodeListElementNodeList 是一个包含多个 XML 节点的列表,Element 是具体的 XML 元素节点。我们通过 getAttribute 方法获取属性值。
4. 解析 节点

我们以类似的方式解析 节点。

    private void parseSettings(Document document) {
        NodeList settingNodes = document.getElementsByTagName("setting");
        for (int i = 0; i < settingNodes.getLength(); i++) {
            Element setting = (Element) settingNodes.item(i);
            settings.put(setting.getAttribute("name"), setting.getAttribute("value"));
        }
    }
  • 解析过程:遍历 节点列表,并将 namevalue 属性值存储在 settings Map 中。
5. 校验配置

解析完 节点后,我们需要对配置进行校验,确保必须的设置项都已定义。

    private void validateConfiguration() {
        if (!settings.containsKey("cacheEnabled")) {
            throw new IllegalArgumentException("缺少必要的设置:cacheEnabled");
        }
    }
  • 校验逻辑:我们检查 settings Map 中是否包含 cacheEnabled 设置项,如果缺少则抛出异常。
6. 提供结果的获取方法

我们提供 getPropertiesgetSettings 方法,让用户能够获取解析后的配置内容。

    public Map<String, String> getProperties() {
        return properties;
    }

    public Map<String, String> getSettings() {
        return settings;
    }
7. 编写主方法测试

最后,我们编写一个简单的 main 方法来测试这个解析器。

    public static void main(String[] args) throws Exception {
        SimpleXMLConfigParser parser = new SimpleXMLConfigParser();
        parser.parse("path/to/mybatis-config.xml");
        System.out.println("Properties: " + parser.getProperties());
        System.out.println("Settings: " + parser.getSettings());
    }
}

完整代码汇总

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class SimpleXMLConfigParser {
    private Map<String, String> properties = new HashMap<>();
    private Map<String, String> settings = new HashMap<>();

    public void parse(String filePath) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new File(filePath));

        parseProperties(document);
        parseSettings(document);
        validateConfiguration();
    }

    private void parseProperties(Document document) {
        NodeList propertyNodes = document.getElementsByTagName("property");
        for (int i = 0; i < propertyNodes.getLength(); i++) {
            Element property = (Element) propertyNodes.item(i);
            properties.put(property.getAttribute("name"), property.getAttribute("value"));
        }
    }

    private void parseSettings(Document document) {
        NodeList settingNodes = document.getElementsByTagName("setting");
        for (int i = 0; i < settingNodes.getLength(); i++) {
            Element setting = (Element) settingNodes.item(i);
            settings.put(setting.getAttribute("name"), setting.getAttribute("value"));
        }
    }

    private void validateConfiguration() {
        if (!settings.containsKey("cacheEnabled")) {
            throw new IllegalArgumentException("缺少必要的设置:cacheEnabled");
        }
    }

    public Map<String, String> getProperties() {
        return properties;
    }

    public Map<String, String> getSettings() {
        return settings;
    }

    public static void main(String[] args) throws Exception {
        SimpleXMLConfigParser parser = new SimpleXMLConfigParser();
        parser.parse("path/to/mybatis-config.xml");
        System.out.println("Properties: " + parser.getProperties());
        System.out.println("Settings: " + parser.getSettings());
    }
}

自定义实现类图

SimpleXMLConfigParser
- Map properties
- Map settings
+parse(String filePath)
+parseProperties(Document document)
+parseSettings(Document document)
+validateConfiguration()
+getProperties()
+getSettings()

代码解析流程图

开始
加载 XML 文件
解析 properties 节点
存储 properties 配置
解析 settings 节点
存储 settings 配置
校验配置
完成解析

源码解析:MyBatis 中的 XMLConfigBuilder

下面是完整的一篇关于 MyBatis 源码解析:XMLConfigBuilder 解析过程 的文章。包括了自定义实现、类图、流程图、源码解析、以及总结的内容。


MyBatis 源码解析:XMLConfigBuilder 解析过程

前言

在 MyBatis 框架中,XMLConfigBuilder 是一个关键类,负责解析 mybatis-config.xml 配置文件。理解这个类的工作原理有助于掌握 MyBatis 的配置管理和框架初始化过程。本篇文章将通过自定义实现一个简化版的 XML 配置解析器,并深入解析 MyBatis 中 XMLConfigBuilder 的实现细节。


自定义实现:简化版 XML 配置解析器

目标与功能

我们将实现一个简化版的 XML 配置解析器,主要功能是解析 节点,并生成一个基本的配置对象。这个实现将帮助我们理解 MyBatis 的配置解析过程,为后续的源码解读打下基础。

XML 样例

在开始实现之前,我们先来看一个典型的 mybatis-config.xml 文件的片段:


DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    properties>

    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="false"/>
    settings>
configuration>
  • 节点:加载外部的属性文件或定义属性。
  • 节点:定义 MyBatis 的全局设置,如缓存启用和懒加载等。

实现过程

1. 定义解析器类

首先,我们创建一个名为 SimpleXMLConfigParser 的类,用于处理 XML 文件的解析。类中定义了两个 Map,分别用于存储 节点的内容。

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class SimpleXMLConfigParser {
    private Map<String, String> properties = new HashMap<>();  // 存储  节点内容
    private Map<String, String> settings = new HashMap<>();    // 存储  节点内容
2. 加载与解析 XML 文件

我们编写 parse 方法,该方法负责加载 XML 文件并依次解析 节点。

    public void parse(String filePath) throws Exception {
        // 创建解析器工厂和解析器
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new File(filePath));  // 解析 XML 文件

        // 解析  节点
        parseProperties(document);
        // 解析  节点
        parseSettings(document);
        // 校验配置的有效性
        validateConfiguration();
    }
  • DocumentBuilderFactoryDocumentBuilder:用于创建 XML 文档解析器。
  • Document:表示整个 XML 文件内容的对象,供我们解析和操作。
3. 解析 节点

接下来,我们编写 parseProperties 方法来解析 节点,并将解析结果存储在 properties Map 中。

    private void parseProperties(Document document) {
        NodeList propertyNodes = document.getElementsByTagName("property");
        for (int i = 0; i < propertyNodes.getLength(); i++) {
            Element property = (Element) propertyNodes.item(i);
            properties.put(property.getAttribute("name"), property.getAttribute("value"));
        }
    }
  • NodeListElementNodeList 是一个包含多个 XML 节点的列表,Element 是具体的 XML 元素节点。我们通过 getAttribute 方法获取属性值。
4. 解析 节点

我们以类似的方式解析 节点。

    private void parseSettings(Document document) {
        NodeList settingNodes = document.getElementsByTagName("setting");
        for (int i = 0; i < settingNodes.getLength(); i++) {
            Element setting = (Element) settingNodes.item(i);
            settings.put(setting.getAttribute("name"), setting.getAttribute("value"));
        }
    }
  • 解析过程:遍历 节点列表,并将 namevalue 属性值存储在 settings Map 中。
5. 校验配置

解析完 节点后,我们需要对配置进行校验,确保必须的设置项都已定义。

    private void validateConfiguration() {
        if (!settings.containsKey("cacheEnabled")) {
            throw new IllegalArgumentException("缺少必要的设置:cacheEnabled");
        }
    }
  • 校验逻辑:我们检查 settings Map 中是否包含 cacheEnabled 设置项,如果缺少则抛出异常。
6. 提供结果的获取方法

我们提供 getPropertiesgetSettings 方法,让用户能够获取解析后的配置内容。

    public Map<String, String> getProperties() {
        return properties;
    }

    public Map<String, String> getSettings() {
        return settings;
    }
7. 编写主方法测试

最后,我们编写一个简单的 main 方法来测试这个解析器。

    public static void main(String[] args) throws Exception {
        SimpleXMLConfigParser parser = new SimpleXMLConfigParser();
        parser.parse("path/to/mybatis-config.xml");
        System.out.println("Properties: " + parser.getProperties());
        System.out.println("Settings: " + parser.getSettings());
    }
}

完整代码汇总

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class SimpleXMLConfigParser {
    private Map<String, String> properties = new HashMap<>();
    private Map<String, String> settings = new HashMap<>();

    public void parse(String filePath) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new File(filePath));

        parseProperties(document);
        parseSettings(document);
        validateConfiguration();
    }

    private void parseProperties(Document document) {
        NodeList propertyNodes = document.getElementsByTagName("property");
        for (int i = 0; i < propertyNodes.getLength(); i++) {
            Element property = (Element) propertyNodes.item(i);
            properties.put(property.getAttribute("name"), property.getAttribute("value"));
        }
    }

    private void parseSettings(Document document) {
        NodeList settingNodes = document.getElementsByTagName("setting");
        for (int i = 0; i < settingNodes.getLength(); i++) {
            Element setting = (Element) settingNodes.item(i);
            settings.put(setting.getAttribute("name"), setting.getAttribute("value"));
        }
    }

    private void validateConfiguration() {
        if (!settings.containsKey("cacheEnabled")) {
            throw new IllegalArgumentException("缺少必要的设置:cacheEnabled");
        }
    }

    public Map<String, String> getProperties() {
        return properties;
    }

    public Map<String, String> getSettings() {
        return settings;
    }

    public static void main(String[] args) throws Exception {
        SimpleXMLConfigParser parser = new SimpleXMLConfigParser();
        parser.parse("path/to/mybatis-config.xml");
        System.out.println("Properties: " + parser.getProperties());
        System.out.println("Settings: " + parser.getSettings());
    }
}

自定义实现类图

SimpleXMLConfigParser
- Map properties
- Map settings
+parse(String filePath)
+parseProperties(Document document)
+parseSettings(Document document)
+validateConfiguration()
+getProperties()
+getSettings()

代码解析流程图

开始
加载 XML 文件
解析 properties节点
存储 properties 配置
解析 settings 节点
存储 settings 配置
校验配置
完成解析

源码解析:MyBatis 中的 XMLConfigBuilder

继续生成完整的内容:


源码解析:MyBatis 中的 XMLConfigBuilder

1. 初始化 XMLConfigBuilder

在 MyBatis 中,XMLConfigBuilder 是用于解析配置文件的核心类。初始化过程主要包括创建 XPathParser 来处理 XML 文件,并初始化 Configuration 对象来存储解析后的配置信息。

public XMLConfigBuilder(InputStream inputStream) {
    this(new XPathParser(inputStream, true, null, new XMLMapperEntityResolver()), null, null);
}

public XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parser = parser;
    this.environment = environment;
}

2. 解析 节点

XMLConfigBuilder 中,propertiesElement 方法用于解析 节点,并将属性加载到 Properties 对象中。这些属性可以来自外部文件,也可以直接在 XML 中定义。

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
        Properties defaults = context.getChildrenAsProperties();
        String resource = context.getStringAttribute("resource");
        String url = context.getStringAttribute("url");

        if (resource != null && url != null) {
            throw new BuilderException("The properties element cannot specify both a URL and a resource-based property file reference.");
        }

        if (resource != null) {
            defaults.putAll(Resources.getResourceAsProperties(resource));
        } else if (url != null) {
            defaults.putAll(Resources.getUrlAsProperties(url));
        }

        Properties vars = configuration.getVariables();
        if (vars != null) {
            defaults.putAll(vars);
        }

        parser.setVariables(defaults);
        configuration.setVariables(defaults);
    }
}
  • 外部属性文件的加载resourceurl 属性允许从外部文件加载属性配置。Resources.getResourceAsPropertiesResources.getUrlAsProperties 方法用于将外部文件的内容加载到 Properties 对象中。
  • 属性合并defaults 对象将合并所有属性,包括外部加载的属性和配置文件中直接定义的属性。最终,这些属性被设置到 XPathParserConfiguration 中,供后续解析使用。

3. 解析 节点

settingsElement 方法用于解析 节点,将各项设置转换为 Properties 对象并应用到 Configuration 中。

private void settingsAsProperties(XNode context) throws Exception {
    if (context != null) {
        Properties props = context.getChildrenAsProperties();
        configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
        configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
        configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
        // 其他设置...
    }
}
详细讲解
  • setAutoMappingBehavior:控制 MyBatis 自动映射结果集的行为。
  • setCacheEnabled:启用或禁用 MyBatis 的一级缓存。
  • setLazyLoadingEnabled:启用或禁用懒加载功能。

settingsElement 方法中,Properties 对象中的设置会依次被应用到 Configuration 对象中。每个设置对应着 MyBatis 的一项全局配置。

4. 解析 节点

MyBatis 的 节点允许配置多个运行环境(如开发、测试和生产),每个环境都有自己的数据源和事务管理器配置。environmentsElement 方法解析此节点,并根据当前配置的环境来选择相应的数据源和事务管理器。

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        String defaultEnvironment = context.getStringAttribute("default");
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            if (isSpecifiedEnvironment(id, defaultEnvironment)) {
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                DataSource dataSource = dsFactory.getDataSource();
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}

5. 完整解析流程

XMLConfigBuilder 解析 mybatis-config.xml 文件的完整流程如下:

  1. 初始化:创建 XPathParserConfiguration 对象。
  2. 解析 节点:加载外部属性文件,并将属性应用到 Configuration 中。
  3. 解析 节点:将全局设置应用到 Configuration 中。
  4. 解析 节点:根据配置的环境选择合适的数据源和事务管理器。
  5. 完成配置:解析完成后,Configuration 对象中包含了所有的 MyBatis 配置信息。

源码流程图

开始
初始化 XMLConfigBuilder
解析 properties 节点
解析 settings节点
解析 environments>节点
完成配置

对比总结

通过对比,我们可以看到,自定义实现和 XMLConfigBuilder 在整体流程上非常相似,但 XMLConfigBuilder 更加复杂和全面。自定义实现主要涵盖了基本的 XML 解析过程,而 MyBatis 的 XMLConfigBuilder 则加入了更多的细节处理和配置管理功能,例如外部资源加载、属性合并、以及对各项设置的全面支持。


总结与互动

通过这篇文章,我们详细解析了 MyBatis 中 XMLConfigBuilder 的工作流程,并通过自定义实现加深了对 XML 配置解析的理解。掌握这些知识有助于我们更好地理解 MyBatis 的初始化和配置管理。

如果您觉得这篇文章对您有帮助,请点赞、收藏并关注!此外,欢迎在评论区留言,与我们分享您的见解或提出疑问!


你可能感兴趣的:(MyBatis,源码解读,mybatis,java)