【MyBatis源码学习】初始化阶段


一、核心配置类

XMLConfigBuilder: 主要负责解析mybatis-config.xml;
XMLMapperBuilder: 主要负责解析映射配置Mapper.xml 文件;
XMLStatementBuilder: 主要负责解析映射配置文件中的SQL 节点
三者之间的关系,如下图

"核心配置类三剑客关系图"

1.XMLConfigBuilder

主要负责解析mybatis关键配置文件mybatis-config.xml,重点关注91行,全局唯一初始化的大配置类。

"图1"

面试题经常会问,为啥mybatis的配置类Configuration是单例的?首先我们在BaseBuilder中发现它是final类型的,其次91行是全局唯一初始化它的地方。

"图2"

2.XMLMapperBuilder

负责解析mapper.xml和mapper.java文件,关键代码

"图3"

重点关注下configurationElement方法

"图4"

3.XMLStatementBuilder

主要负责解析*mapper.xml中的select、insert、update、delete标签语句。重点关注下这个方法

"图5"

4.Configuration

"图6"

二、源码追踪初始化流程

1.准备工作

编写测试类

public class MybatisDemo {
	private SqlSessionFactory sqlSessionFactory;
	
	@Before
	public void init() throws IOException {
		//--------------------第一阶段---------------------------
	    // 1.1 读取mybatis配置文件创SqlSessionFactory
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 1.2 读取mybatis配置文件创SqlSessionFactory
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		inputStream.close();
	}
}

2.源码分析

2.1.第一阶段

2.1.1.生成SqlSessionFactory

// 1.1 读取mybatis配置文件创SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

读取Mybaits的主配置配置文件,并返回该文件的输入流,我们知道Mybatis所有的SQL语句都写在XML配置文件里面,所以第一步就需要读取这些XML配置文件,这个不难理解,关键是读取文件后怎么存。

// 1.2 读取mybatis配置文件创SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

读取配置文件流并将这些配置信息存放到Configuration类中.

2.1.2.解析配置,生成configuration对象

重点关注下SqlSessionFactoryBuilder的build方法,主要分为两类:

//字符流
SqlSessionFactory build(Reader reader);
SqlSessionFactory build(Reader reader, String environment);
SqlSessionFactory build(Reader reader, Properties properties);
SqlSessionFactory build(Reader reader, String environment, Properties properties);
//字节流
SqlSessionFactory build(InputStream inputStream);
SqlSessionFactory build(InputStream inputStream, String environment);
SqlSessionFactory build(InputStream inputStream, Properties properties);
SqlSessionFactory build(InputStream inputStream, String environment, Properties properties);

我们只看InputStream对应的build方法即可,顺带一提的是,其实我们可以通过

Reader reader=new InputStreamReader(inputStream);

来将Inputstream转换为Reader对象。

/**
  * 通过字节流创建 SqlSessionFactory
  *
  * @param inputStream mybatis配置文件对应的字节流(可以通过Reader reader=new InputStreamReader(inputStream);来将Inputstream转换为Reader对象)
  * @param environment 指定当前使用的环境标志
  * @param properties  用户自定义的属性对象
  * @return SqlSessionFactory
  */
 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.
         }
     }
 }

点进去XMLConfigBuilder的构造方法

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	//重点地方,全局唯一构造Configuration对象的地方
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

再去瞧瞧XMLConfigBuilder的parse方法,重点关注parseConfiguration方法。

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 {
    //issue #117 read properties first
   //解析<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"));
    //解析<objectWrapperFactory>节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    //解析<reflectorFactory>节点
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    //将settings填充到configuration
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    //解析<environments>节点
    environmentsElement(root.evalNode("environments"));
    //解析<databaseIdProvider>节点
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    //解析<typeHandlers>节点
    typeHandlerElement(root.evalNode("typeHandlers"));
    //解析<mappers>节点
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

以上是分析

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析配置文件得到configuration对象,并返回SqlSessionFactory
return build(parser.parse()); 

parser.parse()的部分,下面看看build()部分:

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

很简单,就是把读取配置文件后的Configuration对象赋给DefaultSqlSessionFactory,返回一个构建好的sqlSessionFactory,而DefaultSqlSessionFactory是SqlSessionFactory的默认实现.

2.2.第二阶段

// 2.获取sqlSession	
SqlSession sqlSession = sqlSessionFactory.openSession();

通过调用DefaultSqlSessionFactory的openSession方法返回一个SqlSession实例,我们看一下具体是怎么得到一个SqlSession实例的。首先调用openSessionFromDataSource方法。
摸进去看看openSessionFromDataSource():

/**
  * 从数据源获取数据库连接
  *
  * @param execType   执行类型,ExecutorType主要有三种类型:SIMPLE, REUSE, BATCH,默认是SIMPLE,都在枚举类ExecutorType里面
  * @param level      事务隔离级别,都在枚举类TransactionIsolationLevel中定义
  * @param autoCommit 是否自动提交,主要是事务提交的设置
  * @return SqlSession DefaultSqlSession是SqlSession的实现类,该类主要提供操作数据库的方法给开发人员使用
  */
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
   Transaction tx = null;
   try {
   	//获取mybatis配置文件中的environment对象,包括使用哪种数据库,连接数据库的信息,事务
     final Environment environment = configuration.getEnvironment();
     //从environment获取transactionFactory对象
     final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
     //创建事务对象
     tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
     //根据配置创建executor
     final Executor executor = configuration.newExecutor(tx, execType);
     //创建DefaultSqlSession
     return new DefaultSqlSession(configuration, executor, autoCommit);
   } catch (Exception e) {
     // may have fetched a connection so lets call close()
     closeTransaction(tx);
     throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
   } finally {
     ErrorContext.instance().reset();
   }
 }

三、总结

步骤一:读取Mybatis的主配置文件,并将文件读成文件流形式(InputStream)。

步骤二:从主配置文件流中读取文件的各个节点信息并存放到Configuration对象中。读取mappers节点的引用文件,并将这些文件的各个节点信息存放到Configuration对象。

步骤三:根据Configuration对象的信息获取数据库连接,并设置连接的事务隔离级别等信息,将经过包装数据库连接对象SqlSession接口返回,DefaultSqlSession是SqlSession的实现类,所以这里返回的是DefaultSqlSession,SqlSession接口里面就是对外提供的各种数据库操作。


文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录