Mybatis流程分析(二):配置处理,构建SqlSession工厂

1,810 阅读5分钟

本系列文章皆在从细节着手,由浅入深的分析Mybatis框架内部的处理逻辑,带你从一个全新的角度来认识Mybatis的工作原理。

思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜

前言

在上一章Mybatis流程分析(一):揭秘Mybatis对配置文件解析的全流程,我们讨论了以配置文件加载为切入点,讨论了Mybatis内部对于xml文件的解析过程。具体来看,Mybatis对配置文件的解析过程如下图所示。

image.png

可以看到,在配置文件的解析过程中其会首先调用SqlSessionFactortyBuilder中的build方法,接着再调用XMLConfigBuilderparse()方法,并最终返回一个Configuraion对象。进一步,又会调用SqlSessionFactortyBuilder重载build方法,返回一个SqlSessionFactory对象。

这次我们主要来看其中的ConfiguraionSqlSessionFactory究竟藏了哪些秘密

资源配置的大管家——Configuration

在开始分析Configuration之前,我们先看看parse方法是如何返回Configuration对象的。XMLConfigBuilderparse方法逻辑如下所示:

XMLConfigBuilderparse()方法

public Configuration parse() {
    // 解析mybatis中配置的xml文件
    parseConfiguration(parser.evalNode("/configuration"));
    // 返回一个Configuration对象
    return configuration;
  }

可以看到,parse方法所返回的configuration对象为XMLConfigBuilder的成员变量。而parse方法似乎并没有对configuration的进行处理,事实当真如此吗?当然不是!

如果XMLConfigBuilder内部没有对成员变量信息configuration进行进行额外的设置,那在SqlSessionFactoryBuilderbuild逻辑完全可以直接通过getConfigurationXMLConfigBuilder获取成员变量configuration。又何必大非常的调用调用XMLConfigBuilderparse方法?

SqlSessionFactoryBuilder # build

  public SqlSessionFactory build(Reader reader, String environment,   Properties properties) {
    // 构建XMLConfigBuilder对象
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
   
    // 如果不对XMLConfigBuilder中的configuration进行额外处理
    // 此处完全可以改为下述代码
    // return build(parser.getConfiguration());
    
    return build(parser.parse());
}

所以,parse方法内部的parseConfiguration一定会对configuration对象进行处理。

接下来,我们便进入到parseConfiguration中,看看是不是其内部逻辑是不是像我们猜测的那样。

XMLConfigBuilderr # parseConfiguration


private void parseConfiguration(XNode root) {
  // 暂时忽略对于其他标签的处理,重点关注environments标签
  environmentsElement(root.evalNode("environments"));
}

对于Mybatis配置文件<configuration>标签下的environments通常会有如下的配置信息:

<!--Mybatis配置文件 -->
<configuration>
   <environments default="mysql">
       <environment id="mysql">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?serverTimezone=UTC"/>
               <property name="username" value="root"/>
               <property name="password" value="root"/>
           </dataSource>
       </environment>
   </environments>
</configuration>

其中属性的含义如下:

  1. id 配置环境的名称信息
  2. transactionManager 指明数据库事务实现方式
  3. datoSource 配置数据库连接池信息

了解environment标签后,再来看environmentsElement的处理逻辑相信一定会有一种拨云见日的感觉。进一步,environmentsElement的处理逻辑如下:

private void environmentsElement(XNode context) throws Exception {
      // 逐个遍历Environments标签下的environment标签
      for (XNode child : context.getChildren()) {
        // 获取environment的id属性信息
        String id = child.getStringAttribute("id");
        // 获取transactionManager属性
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        // 获取数据源信息
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        // 构建一个EnvironmentBuilder
        Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
        // 通过EnvironmentBuilder构建一个Environment对象,设定到configuration中
    configuration.setEnvironment(environmentBuilder.build());
  }
}

其实,environmentsElement方法背后并没有做什么复杂的逻辑。主要就是对environment标签的属性信息进行解析处理。

当所有信息全部解析完毕后,其会将配置文件中设定的内容设定到Environment对象中;然后,再将配置好的Environment对象设定到XMLConfigBuilder的成员变量Configuration之中。

事实上,

Configuration 对象是一个核心组件,负责管理 MyBatis 的配置信息和各种资源。例如:

  1. 配置信息管理: Configuration 对象负责管理 MyBatis 的全局配置信息,包括数据库连接信息、缓存配置、插件配置等。
  2. 映射信息管理:MyBatis通过 Configuration 对象管理与数据库表的映射关系。映射信息包括 SQL 语句与方法的映射关系,以及查询结果与 Java 对象之间的映射配置。
  3. Mapper 注册: Configuration 对象负责将 Mapper 接口与对应的映射配置关联起来。通过它,你可以将 Mapper 接口与 xml 文件或注解进行关联,从而实现 SQL 语句的映射。

(注:Configuration中还有其他信息,此处仅是列举了几个同开发关系紧密的信息~~)

所以Configuration更像一个 “大管家” 一样,统筹管理着Mybatis的配置信息。 明白了Configuration对象的作用后,我们继续来看parse方法中build(parse.parse())方法的逻辑,方法逻辑如下:

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

上述代码逻辑很简单,就是将配置信息configuration传入build方法,然后new一个DefaultSqlSessionFactory进行返回。

接下来,我们继续来看DefaultSqlSessionFactory的究竟又是何物

SqlSessionFactory相关内容

对于DefaultSqlSessionFactory而言,其有如下的类结构关系:

image.png

可以看到,DefaultSqlSessionFactory类结构关系非常简单。其只实现了一个SqlSessionFactory 接口。而SqlSessionFactory 接口内容如下所示:

public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);
  // 其他为openSession方法的重载

}

可以看到,SqlSessionFactory作为一个接口,其内部只定义了一个openSession方法,而该方法的返回值一个SqlSession对象。

(注:其他方法均为openSession方法的重载版本)

因此对于SqlSessionFactory而言,只需记住:其是一个SqlSession的工厂,通过该工厂可以获取SqlSession对象。

此处实际上是一个工厂模式的典型应用。具体来看,在SqlSessionFactory内部定义了创建SqlSession的创建方式。从而使得SqlSession的创建和使用进行了分析。

(注:SqlSession相关内容我们会在后续文章中分析,此处可以先选择性的 忽视)

DefaultSqlSessionFactory作为SqlSessionFactory的唯一实现。接下来,我们便看看其内部又有哪些逻辑。DefaultSqlSessionFactory的相关内容:

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  // mybatis会将所有配置信息归结到configuration之中
  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }
  /**
   * 获取一个sqlSession信息 
   **/
  @Override
  public SqlSession openSession() {
    // 调用openSessionFromDataSource返回一个SqlSession对象
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  // .......省略其他无关代码
}

可以看到DefaultSqlSessionFactory中的openSession方法,从而将构建SqlSession的逻辑委托给方法openSessionFromDataSource来完成。

总结

至此,我们对MybaitsConfigurationSqlSessionFactory进行了介绍和分析,总结来看:

  1. Configuration用于保存配置文件的配置信息;
  2. SqlSessionFactory作为SqlSession的工厂,用于SqlSession的创建。

此外,解析配置文件、构建SqlSessinonFactory的整体调用过程如下图所示。而这张图可以看做是本文的核心内容的总结。

image.png