当前位置: 首页 > article >正文

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 类,包含环境配置、全局设置、映射器等配置项。

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 对象的生成过程,包括解析配置文件、初始化配置和验证配置。

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 的配置加载过程。

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 缓存等多个部分。

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 对象的生成过程
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)
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)
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)
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。


小可爱提醒

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



http://www.kler.cn/a/287800.html

相关文章:

  • AI驱动的桌面笔记应用Reor
  • Qt / Qt Quick程序打包的一些坑 (四)
  • Python_爬虫3_Requests库网络爬虫实战(5个实例)
  • 如何在 Ubuntu 上安装 Jellyfin 媒体服务器
  • 新手教学系列——善用 VSCode 工作区,让开发更高效
  • jenkins用户在执行scp的时候如何做免密登录
  • 三台机器,第一台机器可以ssh到第二台机器,第二台机器可以ssh到第三台机器,请问第一台机器上怎么通过ssh 直接从第三台机器scp文件到第一台机器?
  • 树数据结构(Tree Data Structures)的全面指南:深度解析、算法实战与应用案例
  • 【WPF】WPF学习之【一】基础知识
  • 62.一个机器人位于一个 m x n 网格的左上角 。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。实现一个算法计算路径的数量
  • 计算机毕业设计python停车场车位推荐管理系统y4uzk
  • “JavaScript里的多线程“WebWorker
  • scikit-learn:一个强大的机器学习Python库
  • APO选择ClickHouse存储Trace的考量
  • 代理IP的API接口:轻松实现自动化代理切换
  • 《软件工程导论》(第6版)第3章 需求分析 复习笔记
  • 同样128个内核,AMD霄龙9755性能翻倍:Zen 5架构下的性能飞跃
  • 【嵌入式学习笔记】STM32中断配置及相关知识
  • Go语言学习(一)
  • SpringBoot链路追踪②:如何集成?
  • Fabric.js中fabric.Text的深入解析
  • linux下部署数据库总结
  • Kubernetes中三种探针的作用你真的知道吗?
  • C语言操作符的介绍
  • 51单片机-独立按键控制LED显示二进制
  • GoF 代理模式