MyBatis初始化基本过程

MyBatis初始化方式

MyBatis初始化提供了两种方式:

  • 基于XML配置文件 :基于XML配置文件的方式是将MyBatis的所有配置信息放在XML文件中mybatis-config.xmlMyBatis通过加载并XML配置文件,将配置文信息组装成内部的Configuration对象
  • 基于Java API :基于Java API的方式是手动创建Configuration对象,然后将配置参数set 进入Configuration对象中。

**  任何框架的初始化,应该都是先加载配置信息,接下来我们将使用基于XML配置文件的方式,来深入讨论MyBatis是如何通过配置文件构建Configuration对象。**

基于Xml配置初始化

**  XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。**

通过一个简单的例子,分析一下基于Xml配置MyBatis是怎样完成初始化的,都做了些什么?

  • mybatis-config.xml配置
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>

    <!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>

    <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>

  </settings>

  <!-- 定义数据库的信息,默认使用development数据库构建环境 -->
  <environments default="development">
    <environment id="development">
      <!-- 使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域 -->
      <transactionManager type="JDBC"/>
      
      <!-- 数据库信息替换为自己的环境 -->
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/work"/>
        <property name="username" value="xxxx"/>
        <property name="password" value="xxxx"/>
      </dataSource>
    </environment>
  </environments>


  <!-- 定义映射器 -->
  <mappers>
    <mapper class="org.apache.ibatis.example.mapper.ScheduleSettingMapper"/>
  </mappers>
</configuration>

MyBatis配置项提供了很多配置项,这个配置文件中只配置了一些基本的节点,只是用来演示。

**  如果有对MyBatis配置项不了解的或者不知道MyBatis提供哪些配置** ,可以去看看MyBatis官网文档,文档上对每一个配置项都已经做出了很详细的说明和示例。

  • 程序入口代码
java 复制代码
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
  inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = null;
//初始化
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

上述代码的功能是通过Resources工具类 ,调用ClassLoader读取classpath下的mybatis-config.xml 配置文件,得到一个输入流inputStream,SqlSessionFactoryBuilder 根据传入的数据流生成Configuration 对象,然后根据Configuration对象创建默认的SqlSessionFactory实例

源码分析

前面提到,SqlSessionFactoryBuilder 根据传入的数据流生成Configuration 对象,然后根据Configuration对象创建默认的SqlSessionFactory实例,现在让我们通过源码来一步一步看一看

  • 调用SqlSessionFactoryBuilder对象的build(inputStream)方法
java 复制代码
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

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

  • SqlSessionFactoryBuilder会根据输入流inputStream等创建XMLConfigBuilder对象
java 复制代码
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 创建XMLConfigBuilder对象用来解析XML配置文件生成Document对象,创建Configuration对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

    // XMLConfigBuilder 对象调用parse()方法,将XML配置文件内的信息解析成Configuration对象  
    Configuration configuration = parser.parse();
    
    // 根据解析好的Configuration对象,创建DefaultSqlSessionFactory对象
    return build(configuration);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      if (inputStream != null) {
        inputStream.close();
      }
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

// 可以通过Configuration创建DefaultSqlSessionFactory对象
public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

通过new XMLConfigBuilder()创建对象时,会生成Configuration 对象和XPathParser对象

  • Configuration对象主要是用来保存xml文件的配置信息
  • XPathParser 对象持有解析mybatis-config.xml 文件和Mapper 文件生成Document对象 和解析mybatis-3-config.dtd 文件和mybatis-3-mapper.dtd 转换成XMLMapperEntityResolver对象
java 复制代码
public XMLConfigBuilder(Class<? extends Configuration> configClass, InputStream inputStream, String environment,
    Properties props) {
  this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
    Properties props) {
  // Configuration的初始化
  super(newConfig(configClass));
  ErrorContext.instance().resource("SQL Mapper Configuration");
  // 设置自定义配置
  this.configuration.setVariables(props);
  // 解析标志
  this.parsed = false;
  // environment初始化
  this.environment = environment;
  // 包装配置 InputStream 的 XPathParser
  this.parser = parser;
}
  • SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法
java 复制代码
public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // 通过XPathParser获取<configuration>节点对应的Node对象
  XNode xNode = parser.evalNode("/configuration");
  // 解析/configuration子节点信息
  parseConfiguration(xNode);
  return configuration;
}

private void parseConfiguration(XNode root) {
  try {
    // 解析properties节点
    propertiesElement(root.evalNode("properties"));
    // 解析settings节点
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    // 配置自定义虚拟文件系统实现
    loadCustomVfsImpl(settings);
    // 配置自定义日志实现
    loadCustomLogImpl(settings);
    // 解析typeAliases节点
    typeAliasesElement(root.evalNode("typeAliases"));
    // 解析plugins节点
    pluginsElement(root.evalNode("plugins"));
    // 解析objectFactory节点
    objectFactoryElement(root.evalNode("objectFactory"));
    // 解析objectWrapperFactory节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 解析reflectorFactory节点
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // 解析environments节点
    environmentsElement(root.evalNode("environments"));
    // 解析databaseIdProvider节点
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    // 解析typeHandlers节点
    typeHandlersElement(root.evalNode("typeHandlers"));
    // 解析mappers节点
    mappersElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

XMLConfigBuilder对象的parse()方法 ,主要是通过XpathParser 根据Xpath表达式获取基本的DOM节点以及子节点Node信息的操作(解析的节点configuration, properties, settings, typeAliases, typeHandlers, objectFactory, objectWrapperFactory, plugins, environments, databaseIdProvider, mappers), 然后将这些值解析出来设置到Configuration对象中,最后返回Configuration对象。

这里的节点解析就不一一去看了,后续会有单独的文章挑几个核心节点做详细介绍。

  • 调用SqlSessionFactoryBuilder对象的build(configuration)方法

通过赋值的Configuration对象,调用build方法创建DefaultSqlSessionFactory对象。基于Java API方式,手动创建XMLConfigBuilder,并解析创建Configuration对象,最后调用此方法生成SqlSessionFactoryBuilder对象。

**  至此,我们就知道了MyBatis是如何通过配置文件构建Configuration对象,并使用它创建SqlSessionFactory对象。**

总结

我们通过一个时序图,把整个myBatis初始化过程串起来,方便小伙伴更加直观的把整个流程串起来,从而对整个初始化过程了解的更加清晰

MyBatis初始化基本过程:

**  每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 从 XML 配置文件或一个预先配置的 Configuration 实例来构建出来。**

相关推荐
吃面不喝汤662 小时前
Flask + Swagger 完整指南:从安装到配置和注释
后端·python·flask
讓丄帝愛伱3 小时前
spring boot启动报错:so that it conforms to the canonical names requirements
java·spring boot·后端
weixin_586062023 小时前
Spring Boot 入门指南
java·spring boot·后端
凡人的AI工具箱9 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀9 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
canonical_entropy9 小时前
金蝶云苍穹的Extension与Nop平台的Delta的区别
后端·低代码·架构
我叫啥都行10 小时前
计算机基础知识复习9.7
运维·服务器·网络·笔记·后端
无名指的等待71211 小时前
SpringBoot中使用ElasticSearch
java·spring boot·后端
Tatakai2511 小时前
Mybatis Plus分页查询返回total为0问题
java·spring·bug·mybatis
.生产的驴11 小时前
SpringBoot 消息队列RabbitMQ 消费者确认机制 失败重试机制
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq