本系列文章皆在从细节着手,由浅入深的分析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
的整体调用过程如下图所示。而这张图可以看做是本文的核心内容的总结。