百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

MyBatis 初始化流程(mybatis配置流程)

ztj100 2024-10-27 18:32 16 浏览 0 评论

之前已经创建了MyBatis的简答项目,今天我们来分析一下MyBatis 基于 XML 配置文件创建 Configuration 对象的过程。

MyBatis 使用 org.apache.ibatis.session.Configuration 对象作为一个存储所有配置信息的容器,Configuration 对象的属性和 mybatis-config.xml 配置文件的组织结构几乎完全一样(当然,Configuration 对象的功能并不限于此,它还负责创建一些 MyBatis 内部使用的对象,如 Executor 等,这些暂不在本文中讨论)。

MyBatis初始化基本过程:

初始化的基本过程如下序列图所示:


读取配置文件

从之前项目(MyBatis架构介绍及简单示例)的Test 主函数第一步可以看出,首先读取 mybatis-config.xml 解析成 Reader。

String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);

创建 SqlSessionFactoryBuilder 对象

从 SqlSessionFactoryBuilder 的名字中可以看出,SqlSessionFactoryBuilder 是用来创建 SqlSessionFactory 对象的。

从 SqlSessionFactoryBuilder 源码可以看出,SqlSessionFactoryBuilder 中只有一些重载的 build 函数,这些 build 函数的入参都是 MyBatis 配置文件的输入流,返回值都是 SqlSessionFactory;由此可见,SqlSessionFactoryBuilder 的作用很纯粹,就是用来通过配置文件创建 SqlSessionFactory 对象的。

public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
    }
    public SqlSessionFactory build(Reader reader, String environment) {
        return build(reader, environment, null);
    }
    public SqlSessionFactory build(Reader reader, Properties properties) {
        return build(reader, null, properties);
    }
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                reader.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }
    public SqlSessionFactory build(InputStream inputStream, String environment) {
        return build(inputStream, environment, null);
    }
    public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return build(inputStream, null, properties);
    }
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

SqlSessionFactory 创建过程

下面是具体如何创建 SqlSessionFactory 对象的 build 函数,通过构造 XMLConfigBuilder 对象,利用其 parse() 方法返回的 Configuration 对象,构建DefaultSqlSessionFactory 对象。

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            reader.close();
        } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
        }
    }
}
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

创建Configuration对象的过程

以上是整个创建 SqlSessionFactory 的流程,而从上面可以看出创建 Configuration 对象,主要是通过 XMLConfigBuilder 对象,调用其 parse() 方法返回 Configuration 对象。

1.构造 XMLConfigBuilder 对象

SqlSessionFactoryBuilder 的 build 函数首先会构造一个 XMLConfigBuilder 对象,该对象是用来解析XML配置文件的。XMLConfigBuilder 的继承自 BaseBuilder, BaseBuilder 有 XMLxxxBuilder 子类,这些 XMLxxxBuilder 是用来解析 XML 配置文件的,不同类型 XMLxxxBuilder 用来解析 MyBatis 配置文件的不同部位。比如:XMLConfigBuilder 用来解析 MyBatis 的配置文件, XMLMapperBuilder 用来解析MyBatis中的映射文件(如上文提到的 UserMapper.xml),XMLStatementBuilder用来解析映射文件中的SQL语句。

当创建 XMLConfigBuilder 对象时,就会初始化 Configuration 对象,并且在初始化 Configuration 对象的时候,一些别名会被注册到 Configuration 的 typeAliasRegistry 容器中。

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}
public Configuration() {
    // 注册别名
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    ……
}

2.解析配置文件,返回 Configuration 对象

当有了 XMLConfigBuilder 对象之后,接下来就可以用它来解析配置文件了。

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 {
        // 解析<properties>节点
        propertiesElement(root.evalNode("properties"));
        // 解析<settings>节点
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        // 解析<typeAliases>节点
        typeAliasesElement(root.evalNode("typeAliases"));
        // 解析<plugins>节点
        pluginElement(root.evalNode("plugins"));
        // 解析<objectFactory>节点
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        // 解析<reflectorFactory>节点
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // 解析<environments>节点
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析<mappers>节点
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

从上述代码中可以看到,XMLConfigBuilder 会依次解析配置文件中的 properties、settings、environments、typeAliases、plugins、mappers 等属性。具体的解析细节不在本文展开,主要是 XMLConfigBuilder 将 XML 配置文件的信息转换为 Document 对象,而 XML 配置定义文件 DTD 转换成 XMLMapperEntityResolver 对象,然后将封装到 XpathParser 对象中,XpathParser 的作用是提供根据 Xpath 表达式获取基本的 DOM 节点 Node 信息的操作,而解析 mappers 节点还使用到了前面提到了 XMLMapperBuilder。

总结

上述的初始化过程中,涉及到了以下几个对象:

SqlSessionFactoryBuilder: SqlSessionFactory 的构造器,用于创建 SqlSessionFactory ,采用了建造设计模式;

SqlSessionFactory: SqlSession 工厂类,以工厂形式创建 SqlSession 对象,采用了工厂设计模式;

XMLConfigBuilder: 负责将 mybatis-config.xml 配置文件解析成 Configuration 对象,供 SqlSessonFactoryBuilder 使用,创建 SqlSessionFactory;

Configuration: 该对象是 mybatis-config.xml 文件中所有 MyBatis 配置信息。

以上就是 MyBatis 初始化创建 SqlSessionFactory 完整流程,对于具体每个节点的解析逻辑后面文章再继续深入分析。

参考文献:

  1. 《MyBatis 技术内幕》
  2. 《Mybatis从入门到精通》

相关推荐

如何将数据仓库迁移到阿里云 AnalyticDB for PostgreSQL

阿里云AnalyticDBforPostgreSQL(以下简称ADBPG,即原HybridDBforPostgreSQL)为基于PostgreSQL内核的MPP架构的实时数据仓库服务,可以...

Python数据分析:探索性分析

写在前面如果你忘记了前面的文章,可以看看加深印象:Python数据处理...

CSP-J/S冲奖第21天:插入排序

...

C++基础语法梳理:算法丨十大排序算法(二)

本期是C++基础语法分享的第十六节,今天给大家来梳理一下十大排序算法后五个!归并排序...

C 语言的标准库有哪些

C语言的标准库并不是一个单一的实体,而是由一系列头文件(headerfiles)组成的集合。每个头文件声明了一组相关的函数、宏、类型和常量。程序员通过在代码中使用#include<...

[深度学习] ncnn安装和调用基础教程

1介绍ncnn是腾讯开发的一个为手机端极致优化的高性能神经网络前向计算框架,无第三方依赖,跨平台,但是通常都需要protobuf和opencv。ncnn目前已在腾讯多款应用中使用,如QQ,Qzon...

用rust实现经典的冒泡排序和快速排序

1.假设待排序数组如下letmutarr=[5,3,8,4,2,7,1];...

ncnn+PPYOLOv2首次结合!全网最详细代码解读来了

编辑:好困LRS【新智元导读】今天给大家安利一个宝藏仓库miemiedetection,该仓库集合了PPYOLO、PPYOLOv2、PPYOLOE三个算法pytorch实现三合一,其中的PPYOL...

C++特性使用建议

1.引用参数使用引用替代指针且所有不变的引用参数必须加上const。在C语言中,如果函数需要修改变量的值,参数必须为指针,如...

Qt4/5升级到Qt6吐血经验总结V202308

00:直观总结增加了很多轮子,同时原有模块拆分的也更细致,估计为了方便拓展个管理。把一些过度封装的东西移除了(比如同样的功能有多个函数),保证了只有一个函数执行该功能。把一些Qt5中兼容Qt4的方法废...

到底什么是C++11新特性,请看下文

C++11是一个比较大的更新,引入了很多新特性,以下是对这些特性的详细解释,帮助您快速理解C++11的内容1.自动类型推导(auto和decltype)...

掌握C++11这些特性,代码简洁性、安全性和性能轻松跃升!

C++11(又称C++0x)是C++编程语言的一次重大更新,引入了许多新特性,显著提升了代码简洁性、安全性和性能。以下是主要特性的分类介绍及示例:一、核心语言特性1.自动类型推导(auto)编译器自...

经典算法——凸包算法

凸包算法(ConvexHull)一、概念与问题描述凸包是指在平面上给定一组点,找到包含这些点的最小面积或最小周长的凸多边形。这个多边形没有任何内凹部分,即从一个多边形内的任意一点画一条线到多边形边界...

一起学习c++11——c++11中的新增的容器

c++11新增的容器1:array当时的初衷是希望提供一个在栈上分配的,定长数组,而且可以使用stl中的模板算法。array的用法如下:#include<string>#includ...

C++ 编程中的一些最佳实践

1.遵循代码简洁原则尽量避免冗余代码,通过模块化设计、清晰的命名和良好的结构,让代码更易于阅读和维护...

取消回复欢迎 发表评论: