本系列文章皆在从细节着手,由浅入深的分析Mybatis框架内部的处理逻辑,带你从一个全新的角度来认识Mybatis的工作原理。
思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜
前言
在上一章Mybatis流程分析(一):揭秘Mybatis对配置文件解析的全流程,我们讨论了以配置文件加载为切入点,讨论了Mybatis内部对于xml文件的解析过程。具体来看,Mybatis对配置文件的解析过程如下图所示。
可以看到,在配置文件的解析过程中其会首先调用SqlSessionFactortyBuilder中的build方法,接着再调用XMLConfigBuilder的parse()方法,并最终返回一个Configuraion对象。进一步,又会调用SqlSessionFactortyBuilder中重载的build方法,返回一个SqlSessionFactory对象。
这次我们主要来看其中的Configuraion和SqlSessionFactory究竟藏了哪些秘密。
资源配置的大管家——Configuration
在开始分析Configuration之前,我们先看看parse方法是如何返回Configuration对象的。XMLConfigBuilder的parse方法逻辑如下所示:
XMLConfigBuilder的parse()方法
public Configuration parse() {
// 解析mybatis中配置的xml文件
parseConfiguration(parser.evalNode("/configuration"));
// 返回一个Configuration对象
return configuration;
}
可以看到,parse方法所返回的configuration对象为XMLConfigBuilder的成员变量。而parse方法似乎并没有对configuration的进行处理,事实当真如此吗?当然不是!
如果XMLConfigBuilder内部没有对成员变量信息configuration进行进行额外的设置,那在SqlSessionFactoryBuilder中build逻辑完全可以直接通过getConfiguration从XMLConfigBuilder获取成员变量configuration。又何必大非常的调用调用XMLConfigBuilder的parse方法?
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>
其中属性的含义如下:
id配置环境的名称信息transactionManager指明数据库事务实现方式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 的配置信息和各种资源。例如:
- 配置信息管理:
Configuration对象负责管理MyBatis的全局配置信息,包括数据库连接信息、缓存配置、插件配置等。 - 映射信息管理:
MyBatis通过Configuration对象管理与数据库表的映射关系。映射信息包括 SQL 语句与方法的映射关系,以及查询结果与Java对象之间的映射配置。 - 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而言,其有如下的类结构关系:
可以看到,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来完成。
总结
至此,我们对Mybaits的Configuration和SqlSessionFactory进行了介绍和分析,总结来看:
Configuration用于保存配置文件的配置信息;SqlSessionFactory作为SqlSession的工厂,用于SqlSession的创建。
此外,解析配置文件、构建SqlSessinonFactory的整体调用过程如下图所示。而这张图可以看做是本文的核心内容的总结。