Mybatis源码解读:初始化流程

50 阅读4分钟

前言

上面那篇文章我们知道Mybatis和数据库的交互方式有Java和Mapper接口两种,所以Mybatis的初始化方式必然也有两种;本篇我们开始对Mybatis的初始化过程进行详细讲解

  • 基于XML配置文件:基于XML配置文件的方式是将Mybatis的所有配置放在XML文件中,Mybatis通过加载XML配置文件,将配置文信息组装成内部的Configuration对象
  • 基于Java API:这种方式不使用XML配置文件,需要Mybatis使用者在Java代码中,手动创建Configuration对象,然后将配置参数set入该对象

XML配置——初始化

我们将通过基于XML配置文件方式的Mybatis初始化,深入探讨Mybatis是如何配置文件构建Configuration对象的

// mybatis初始化
String resource = "mybatis-config.xml";  
InputStream inputStream = Resources.getResourceAsStream(resource);  
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(); 
// 执行SQL
List list = sqlSession.selectList("com.alibaba.tao.xx");

这是一个简单的执行 com.alibaba.tao.xx SQL查询的语句:

经历了三个阶段:

  • mybatis初始化
  • 创建SqlSession
  • 执行SQL语句

上述代码主要的功能是根据配置文件mybatis-config.xml配置文件,创建SqlSessionFactory对象,然后产生SqlSession,执行SQL语句。而mybatis的初始化就发生在第三句:new SqlSessionFactoryBuilder().build(inputStream);我们看看第三句发生了什么:

Mybatis初始化基本过程

SqlSessionFactorBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactor对象,时序图如下:

由上图可以看出,mybatis初始化要经过简单的以下几个步:

  • 调用SqlSessionFactorBuilder对象的build(inputStream)方法;

  • SqlSessionFactorBuilder根据输入流inputStream等信息创建XMLConfigBuilder对象;

  • SqlSessionFactorBuilder调用XMLConfigBuilder对象的parse()方法;

  • XMLConfigBuilder返回Configuration对象;

  • SqlSessionFactorBuilder根据Configuration对象创建一个DefaultSessionFactory对象;

  • SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。

    public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    //1. 创建XMLConfigBuilder对象用来解析XML配置文件,生成Configuration对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    //2. 将XML配置文件内的信息解析成Java对象Configuration对象
    Configuration config = parser.parse();
    //3. 根据Configuration对象创建出SqlSessionFactory对象
    return build(config);
    } 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.
    }
    } }

    // 从此处可以看出,MyBatis内部通过Configuration对象来创建SqlSessionFactory,用户也可以自己通过API构造好Configuration对象,调用此方法创SqlSessionFactory
    public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
    }

上面的代码有一个很重要的类:XMLConfigBuilder,将mybatis-config.xml配置文件解析成Configuration对象,供SqlSessionFactoryBuilder使用,创建SqlSessionFactory,下面重点讲解是如何创建的

创建Configuration对象

现在我们开始分析下parse()方法是如何处理XML文件,生成Configuration对象

先看下Mybatis的配置文件示例

详细的配置信息解释见:www.cnblogs.com/BlogNetSpac…

<configuration>
  <?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>
  <properties resource="db.properties" />
  <typeAliases>
    <typeAlias type="com.example.User" alias="User" />
  </typeAliases>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC" />
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="com/example/mapper/UserMapper.xml" />
  </mappers>
</configuration>

再看下源码

public Configuration parse() {  
    if (parsed) {  
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");  
    }  
    parsed = true;  
    //1.先将xml以及配置文件转换成对应的document对象,构成XNode树结构
    //2.从中解析里面的子Node:properties、typeAliases、plugins等
    parseConfiguration(parser.evalNode("/configuration"));  
    return configuration;  
}  
/** 
 * 解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中 
 */  
private void parseConfiguration(XNode root) {  
    try {  
        //1.首先处理properties 节点     
        propertiesElement(root.evalNode("properties")); //issue #117 read properties first  
        //2.处理typeAliases  
        typeAliasesElement(root.evalNode("typeAliases"));  
        //3.处理插件  
        pluginElement(root.evalNode("plugins"));  
        //4.处理objectFactory  
        objectFactoryElement(root.evalNode("objectFactory"));  
        //5.objectWrapperFactory  
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));  
        //6.settings  
        settingsElement(root.evalNode("settings"));  
        //7.处理environments  
        environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631  
        //8.database  
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));  
        //9.typeHandlers  
        typeHandlerElement(root.evalNode("typeHandlers"));  
        //10.mappers  
        mapperElement(root.evalNode("mappers"));  
    } catch (Exception e) {  
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);  
    }  
} 

上述代码中,我们可以看出,在配置文件中,代码逐渐对以下节点进行解析:

  • configuration节点为根节点。
  • 在configuration节点之下,可以配置10个子节点, 分别为:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings

篇章问题,各个节点解析过程暂不展开赘述

其中,mapperElement(root.evalNode("mappers")); 就是解析XML配置文件子节点的方法,后面篇章展开讲解。

相关的时许图参考如下

基于Java API—初始化

下面是一个基于API方式初始化示例,可以看出就是使用使用XMLConfigBuilder手动解析XML配置文件来创建Configuration对象

String resource = "mybatis-config.xml";  
InputStream inputStream = Resources.getResourceAsStream(resource);  
// 手动创建XMLConfigBuilder,并解析创建Configuration对象  
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null,null); // 看这里 
Configuration configuration = parser.parse();  
// 使用Configuration对象创建SqlSessionFactory  
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);  
// 使用MyBatis  
SqlSession sqlSession = sqlSessionFactory.openSession();  
List list = sqlSession.selectList("com.alibaba.tao.xx");