Bootstrap

【MyBatis源码】SqlSessionFactoryBuilder源码分析

概述

SqlSessionFactory 是 MyBatis 的核心接口之一,提供创建 SqlSession 的方法。SqlSession 则是与数据库交互的主要接口,负责执行 SQL 命令和映射结果。SqlSessionFactoryBuilder 类的作用就是从 XML 配置文件或者其他配置源中加载配置信息,构建出 SqlSessionFactory 实例。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);

类结构

在这里插入图片描述
SqlSessionFactoryBuilder 的基本结构如上,可以看出该类主要提供的就是build方法,但是参数不同,主要有3个不同参数类型的构建。
build(Reader reader):解析配置文件
build(InputStream inputStream):解析配置流
build(Configuration config):从 Configuration 对象构建 SqlSessionFactory

从 InputStream 创建 SqlSessionFactory

接下来我们将深入解析 SqlSessionFactoryBuilder 中的 build(InputStream inputStream) 方法的实现及其背后的细节。

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // XMLConfigBuilder用来解析XML配置文件
      // 使用构建者模式
      // parser.parse()使用XPATH解析XML对象,将配置文件转换为Configuration对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // new DefaultSqlSessionFactory,该对象持有Configuration对象
      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.
      }
    }
  }

【方法参数说明】
• InputStream inputStream: 用于读取 XML 配置文件的输入流。这个流包含了 MyBatis 的配置信息,通常来自于 mybatis-config.xml 文件。
• String environment: 指定当前使用的环境。MyBatis 支持多种环境配置,例如开发、测试和生产。这个参数用于从配置中选择合适的数据库连接设置。具体使用那个由配置决定。
在这里插入图片描述
• Properties properties: 用于传递额外的配置信息。可以通过 Properties 对象向配置文件中添加一些动态的参数,便于灵活配置。

XMLConfigBuilder构建Configuration

MyBatis通过XMLConfigBuilder类完成Configuration对象的构建工作。下面是通过XMLConfigBuilder类创建Configuration的案例代码:

  /**
   * 测试XMLConfigBuilder类完成Configuration对象的构建
   */
  @Test
  public void test5() throws Exception {
    InputStream resource = Resources.getResourceAsStream(MybatisTest.class.getClassLoader(), "mybatis-config.xml");
    XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(resource);
    Configuration parse = xmlConfigBuilder.parse();
    System.out.println(parse);
  }

在这里插入图片描述

XMLConfigBuilder初始化方法

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    // XPathParser是基于java path解析器,用于解析Mybatis中的配置文件
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }
  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    // 解析XML文档为Document对象
    this.document = createDocument(new InputSource(inputStream));
  }

继续回到上一层代码,看XMLConfigBuilder的this()。super(new Configuration());
调用父类构造函数:创建一个新的 Configuration 对象,并将其传递给父类构造函数,表明当前 XMLConfigBuilder 的实例将使用这个全局配置对象。
在这里插入图片描述
在这里插入图片描述
以上的全局变量可以在mybatis的官方文档找到解释说明
在这里插入图片描述
https://mybatis.org/mybatis-3/zh_CN/configuration.html

MyBatis 的 Configuration 类是全局配置文件的核心,负责存储和管理应用程序的各种配置信息
Configuration 构造函数的主要作用是初始化类型别名注册器、设置默认的事务工厂、数据源、缓存策略、日志实现和语言驱动,确保 MyBatis 配置的灵活性和易用性。

parse()方法

继续看XMLConfigBuilder类parse()方法的实现,代码如下:


  public Configuration parse() {
    // 防止parse()方法被同一个实例多次调用
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // parser.evalNode("/configuration") 获取配置文件configuration节点XNode对象
    // parseConfiguration:解析配置文件各个节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
parseConfiguration

继续看parseConfiguration核心方法
在这里插入图片描述
在parseConfiguration()方法中,对于标签的子节点,都有一个单独的方法处理,例如使用propertiesElement()方法解析标签,使用pluginElement()方法解析标签。MyBatis主配置文件中所有标签的用途如下。

具体配置这里可以对着MyBatis 的配置文件官方文档进行参考
https://mybatis.org/mybatis-3/zh_CN/configuration.html

属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。
也可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值。例如:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);

// ... 或者 ...

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);

:用于配置MyBatis数据连接相关的环境及事务管理器信息。通过该标签可以配置多个环境信息,然后指定具体使用哪个。标签的配置信息如下:

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>
environmentsElement(root.evalNode("environments"));

在这里插入图片描述
【序号1】这里的isSpecifiedEnvironment主要是主要用于判断当前指定的环境是否与传入的环境 ID 匹配,如果配置了多个environment标签组,则选择与环境 ID 匹配的那个进行加载处理。这个方法主要在环境配置的上下文中使用,例如在解析 XML 配置文件时,需要确保当前配置与指定的环境匹配。这对于支持多环境配置(如开发、测试、生产等)非常重要。
【序号2】这段代码是 MyBatis 中用于处理事务工厂配置的一个方法,主要负责从 XML 配置中解析并创建 TransactionFactory 实例。
在这里插入图片描述
【序号3】dataSourceElement用于解析获取数据库连接相关的配置信息。

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      // 获取<dataSource>子节点的配置
      Properties props = context.getChildrenAsProperties();
      // 根据getStringAttribute("type");获取对应的实现DataSourceFactory
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      // 映射转换数据源配置 driver;url;username;password
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

在这里插入图片描述
更多使用参见官方文档

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;