一、核心配置类
XMLConfigBuilder: 主要负责解析mybatis-config.xml;
XMLMapperBuilder: 主要负责解析映射配置Mapper.xml 文件;
XMLStatementBuilder: 主要负责解析映射配置文件中的SQL 节点
三者之间的关系,如下图
1.XMLConfigBuilder
主要负责解析mybatis关键配置文件mybatis-config.xml,重点关注91行,全局唯一初始化的大配置类。
面试题经常会问,为啥mybatis的配置类Configuration是单例的?首先我们在BaseBuilder中发现它是final类型的,其次91行是全局唯一初始化它的地方。
2.XMLMapperBuilder
负责解析mapper.xml和mapper.java文件,关键代码
重点关注下configurationElement方法
3.XMLStatementBuilder
主要负责解析*mapper.xml中的select、insert、update、delete标签语句。重点关注下这个方法
4.Configuration
二、源码追踪初始化流程
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接口里面就是对外提供的各种数据库操作。