MyBatis 源码解析:Configuration 对象的生成与初始化

前言

在 MyBatis 框架中,Configuration 对象是核心配置的载体,它包含了所有与 MyBatis 运行相关的配置信息,如环境配置、映射器、SQL 语句缓存等。理解 Configuration 对象的生成与初始化过程,对于深入掌握 MyBatis 的工作原理非常重要。

本篇文章将通过自定义实现一个详细的 Configuration 类,模拟 MyBatis 中的配置管理,随后深入解析 MyBatis 源码中 Configuration 对象的生成与初始化细节。希望通过这些内容,帮助你全面理解 MyBatis 的配置管理机制。


自定义实现:详细的 Configuration 类

目标与功能

我们将实现一个详细的 Configuration 类,主要功能包括:

  1. 管理 MyBatis 的全局配置项。
  2. 管理环境配置和数据源。
  3. 管理映射器、SQL 语句等配置项。
  4. 提供接口进行配置的初始化、验证和使用。

实现过程

1. 定义详细版 Configuration 类

我们将实现一个类似 MyBatis 的 Configuration 类,包含环境配置、全局设置、映射器等配置项。

java 复制代码
import java.util.HashMap;
import java.util.Map;

/**
 * Configuration 类模拟 MyBatis 中的详细配置管理。
 */
public class DetailedConfiguration {
    private String environment;  // 当前环境
    private Map<String, String> settings = new HashMap<>();  // 全局配置项
    private Map<String, String> mappers = new HashMap<>();  // 映射器配置
    private Map<String, String> sqlStatements = new HashMap<>();  // SQL 语句配置

    /**
     * 设置当前环境。
     * @param environment 环境名称
     */
    public void setEnvironment(String environment) {
        this.environment = environment;
    }

    /**
     * 添加一个全局配置项。
     * @param key 配置项名称
     * @param value 配置项值
     */
    public void addSetting(String key, String value) {
        settings.put(key, value);
    }

    /**
     * 添加一个映射器配置。
     * @param name 映射器名称
     * @param mapperClass 映射器类
     */
    public void addMapper(String name, String mapperClass) {
        mappers.put(name, mapperClass);
    }

    /**
     * 添加一个 SQL 语句配置。
     * @param id SQL 语句的唯一标识
     * @param sql SQL 语句内容
     */
    public void addSqlStatement(String id, String sql) {
        sqlStatements.put(id, sql);
    }

    public String getEnvironment() {
        return environment;
    }

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

    public Map<String, String> getMappers() {
        return mappers;
    }

    public Map<String, String> getSqlStatements() {
        return sqlStatements;
    }

    /**
     * 验证配置的完整性和正确性。
     */
    public void validate() {
        if (environment == null || environment.isEmpty()) {
            throw new IllegalStateException("Environment must be set");
        }
        if (settings.isEmpty()) {
            throw new IllegalStateException("Global settings must be provided");
        }
        if (mappers.isEmpty()) {
            throw new IllegalStateException("At least one mapper must be provided");
        }
        if (sqlStatements.isEmpty()) {
            throw new IllegalStateException("At least one SQL statement must be provided");
        }
        System.out.println("Configuration validated successfully.");
    }
}
2. 初始化配置

我们实现一个 ConfigurationBuilder 类,用于模拟 MyBatis 中 Configuration 对象的生成过程,包括解析配置文件、初始化配置和验证配置。

java 复制代码
public class ConfigurationBuilder {
    private DetailedConfiguration configuration = new DetailedConfiguration();

    /**
     * 模拟从配置文件中加载环境配置。
     */
    public void loadEnvironment(String environment) {
        configuration.setEnvironment(environment);
        System.out.println("Environment set to: " + environment);
    }

    /**
     * 模拟从配置文件中加载全局配置项。
     */
    public void loadSettings() {
        configuration.addSetting("cacheEnabled", "true");
        configuration.addSetting("lazyLoadingEnabled", "false");
        configuration.addSetting("mapUnderscoreToCamelCase", "true");
        System.out.println("Settings loaded.");
    }

    /**
     * 模拟从配置文件中加载映射器配置。
     */
    public void loadMappers() {
        configuration.addMapper("UserMapper", "com.example.mybatis.UserMapper");
        configuration.addMapper("OrderMapper", "com.example.mybatis.OrderMapper");
        System.out.println("Mappers loaded.");
    }

    /**
     * 模拟从配置文件中加载 SQL 语句配置。
     */
    public void loadSqlStatements() {
        configuration.addSqlStatement("findUserById", "SELECT * FROM users WHERE id = ?");
        configuration.addSqlStatement("findAllOrders", "SELECT * FROM orders");
        System.out.println("SQL Statements loaded.");
    }

    /**
     * 构建并验证 Configuration 对象。
     * @return 完整的 Configuration 对象
     */
    public DetailedConfiguration build() {
        configuration.validate();
        System.out.println("Configuration built.");
        return configuration;
    }
}
3. 使用示例

我们通过 ConfigurationBuilder 来构建并初始化 DetailedConfiguration 对象,模拟 MyBatis 的配置加载过程。

java 复制代码
public class Main {
    public static void main(String[] args) {
        ConfigurationBuilder builder = new ConfigurationBuilder();

        // 加载环境配置
        builder.loadEnvironment("development");

        // 加载全局设置
        builder.loadSettings();

        // 加载映射器配置
        builder.loadMappers();

        // 加载 SQL 语句配置
        builder.loadSqlStatements();

        // 构建并验证 Configuration 对象
        DetailedConfiguration configuration = builder.build();

        // 打印配置内容
        System.out.println("Current Environment: " + configuration.getEnvironment());
        System.out.println("Settings: " + configuration.getSettings());
        System.out.println("Mappers: " + configuration.getMappers());
        System.out.println("SQL Statements: " + configuration.getSqlStatements());
    }
}

自定义实现总结

通过以上的实现,我们创建了一个详细的 Configuration 类,模拟了 MyBatis 中配置对象的生成、初始化和验证过程。这个实现更接近 MyBatis 的实际配置结构,为理解 MyBatis 中 Configuration 的作用和工作原理提供了一个扎实的基础。

类图与流程图

自定义实现类图

creates DetailedConfiguration - String environment - Map settings - Map mappers - Map sqlStatements +setEnvironment(String environment) +addSetting(String key, String value) +addMapper(String name, String mapperClass) +addSqlStatement(String id, String sql) +validate() +getEnvironment() +getSettings() +getMappers() +getSqlStatements() ConfigurationBuilder - DetailedConfiguration configuration +loadEnvironment(String environment) +loadSettings() +loadMappers() +loadSqlStatements() +build()

自定义实现流程图

开始 定义 DetailedConfiguration 类 实现 ConfigurationBuilder 类 加载环境配置 加载全局设置 加载映射器配置 加载 SQL 语句配置 构建并验证 Configuration 对象 完成


源码解析:MyBatis 中的 Configuration

1. Configuration 类的结构

在 MyBatis 中,Configuration 类非常庞大,负责管理 MyBatis 的所有配置。它包含了环境配置、全局设置、映射器、SQL 缓存等多个部分。

java 复制代码
public class Configuration {
    protected Environment environment;
    protected boolean cacheEnabled = true;
    protected boolean lazyLoadingEnabled = false;
    protected Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

    // 其他配置项和方法...
}
代码详解
  • environment:当前的运行环境,通常包括数据源和事务管理器。
  • cacheEnabledlazyLoadingEnabled:全局配置项,控制 MyBatis 的缓存和懒加载功能。
  • mappedStatements:存储映射器中的 SQL 语句配置。

2. Configuration 对象的生成

在 MyBatis 中,XMLConfigBuilder 类负责解析 MyBatis 的核心配置文件 `mybatis-config

.xml,并生成 Configuration` 对象。这个过程包括解析环境配置、全局设置、映射器以及 SQL 语句等多个方面。核心步骤包括:

  1. 解析 <environments> 节点,初始化 Environment 对象。
  2. 解析 <settings> 节点,加载全局设置。
  3. 解析 <mappers> 节点,加载映射器配置。
  4. 将所有配置信息封装到 Configuration 对象中。
Configuration 对象的生成过程
java 复制代码
public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    try {
        // 解析环境配置
        environmentsElement(root.evalNode("environments"));
        // 解析全局设置
        settingsElement(root.evalNode("settings"));
        // 解析映射器配置
        mapperElement(root.evalNode("mappers"));
        // 其他解析逻辑...
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}
代码详解
  • parse() 方法 :这是 XMLConfigBuilder 中的一个核心方法,负责启动配置文件的解析流程。如果已经解析过,则抛出异常,以确保每个 XMLConfigBuilder 对象只解析一次。
  • parseConfiguration(XNode root) 方法:根据配置文件的根节点依次解析各个配置项,包括环境、全局设置和映射器等。

3. 配置初始化的细节

每个配置部分在 XMLConfigBuilder 中都有对应的方法进行解析和初始化:

解析环境配置 (environmentsElement)
java 复制代码
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());
            }
        }
    }
}
代码详解
  • defaultEnvironment :从 <environments> 节点中获取默认的环境 ID。
  • transactionManagerElementdataSourceElement :分别解析 <transactionManager><dataSource> 节点,返回对应的 TransactionFactoryDataSource 对象。
  • isSpecifiedEnvironment:判断当前解析的环境 ID 是否为默认环境。
  • configuration.setEnvironment :最终将解析后的 Environment 对象设置到 MyBatis 的 Configuration 中。
解析全局设置 (settingsElement)
java 复制代码
private void settingsElement(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:设置自动映射的行为方式。
  • setCacheEnabledsetLazyLoadingEnabled:设置 MyBatis 的缓存和懒加载功能。
  • context.getChildrenAsProperties() :将 <settings> 节点下的所有子节点解析为 Properties 对象。
解析映射器 (mapperElement)
java 复制代码
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            String resource = child.getStringAttribute("resource");
            String url = child.getStringAttribute("url");
            String mapperClass = child.getStringAttribute("class");

            if (resource != null && url == null && mapperClass == null) {
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                mapperParser.parse();
            } else if (url != null && resource == null && mapperClass == null) {
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                mapperParser.parse();
            } else if (mapperClass != null && resource == null && url == null) {
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
            }
        }
    }
}
代码详解
  • resourceurl :从 <mapper> 节点中获取 XML 映射文件的路径或 URL。
  • mapperClass:直接通过类名指定映射器接口。
  • XMLMapperBuilder :用于解析映射器 XML 文件,并将其映射到 Configuration 中。

4. 总结

在这篇文章中,我们通过自定义实现与 MyBatis 源码解析,深入理解了 Configuration 对象的生成与初始化过程。Configuration 作为 MyBatis 的核心配置类,管理着框架的几乎所有运行参数。掌握这些知识点,将有助于你在实际项目中更好地应用 MyBatis。


小可爱提醒

如果你觉得这篇文章对你有帮助,记得点个小⭐收藏哦!关注我,不错过更多精彩内容!要是有什么疑问或想法,快来评论区和我一起讨论吧!😊


相关推荐
wywcool2 分钟前
JVM学习之路(5)垃圾回收
java·jvm·后端·学习
-seventy-21 分钟前
Java Web 工程全貌
java
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ27 分钟前
idea 删除本地分支后,弹窗 delete tracked brank
java·ide·intellij-idea
言慢行善28 分钟前
idea出现的问题
java·ide·intellij-idea
杨荧38 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的宠物咖啡馆平台
java·开发语言·jvm·vue.js·spring boot·spring cloud·开源
Firechou44 分钟前
SpringBoot+MyBatis+MySQL的Point实现范围查找
spring boot·mysql·mybatis·point·范围查找·附近查找
Ling_suu1 小时前
Spring——单元测试
java·spring·单元测试
ModelBulider1 小时前
十三、注解配置SpringMVC
java·开发语言·数据库·sql·mysql
苹果酱05671 小时前
C语言 char 字符串 - C语言零基础入门教程
java·开发语言·spring boot·mysql·中间件
csucoderlee2 小时前
eclipse mat leak suspects report和 component report的区别
java·ide·eclipse