从 Mybatis 源码到 Spring 动态数据源底层原理分析系列一、Mybatis 初始化源码浅析
注意事项, mybatis 源码相对于之前我分析的 spring、springmvc 的源码来说, 会更加的简单, 但是我不会把每一个细节都分析的非常底层, 而是从一个整体的流程出发进行分析, 在对 mybatis 有一个整体的认知之后, 如果想深入的了解某一块的内容, 那么会变得非常轻松, 举个例子, 对于如何将一个<select>这样的标签解析并得到对应的实体类对象这样的内容不会进行分析(这里面其实就是一些 xml 的解析以及对嵌套查询的处理而已)
二、配置文件整体概览
2.1、简单的描述
我们以 xml 格式下的 mybatis 文件进行分析, 这是相对比较传统的, 而目前大家都是基于 springboot 进行开发, 在这种情况下, 只不过是将对 mybatis 配置文件的解析变成了对 application.yml 文件的解析, 进而得到对应的 mybatis 配置对象而已, 之后我们在分析 spring 整合 mybatis 的时候, 也会分析这一块, 从 xml 文件入手, 我们之后会能够更加的清晰 springboot 是如何剔除了 mybatis-config 这样的配置文件的
2.2、配置文件
------ mybatis-con.xml ------
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://dev1-linux.pospal.cn:3306/pospal?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=true&allowMultiQueries=true"/>
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/CustomerMapper.xml"/>
</mappers>
</configuration>
------ CustomerMapper.xml ------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fightzhong.mapper.CustomerMapper">
<select id="selectRemarksById" parameterType="int" resultType="String" >
select remarks
from customer where id = #{id}
</select>
</mapper>
复制代码
2.3、config 文件分析
其实 mybatis 在初始化的时候就是对上面这些配置文件进行 xml 解析而已, 将它们解析成一个个的 java 对象存储起来, 首先我们来看看 mybatis-con.xml 这个文件, configuration 对象, 故名思义, 就是 mybatis 的配置, 在 mybatis 初始化的时候, 会将其解析成一个 Configuration 对象, 而 environment 对象也是一样, 会将其解析成一个 Environment 对象, environments 标签中可以配置多个 environment 标签, 通过 default 来表示默认生效哪一个, 在 environment 中, 则表示的是当前环境下的数据库配置信息, 其中包括事务工厂(用于创建事务的 factory)、数据源等, 所以以上配置映射成 java 对象即如下:
class Configuration {
private Environment environment;
private Map<String, MappedStatement> mappedStatements;
}
class Environment {
private TransactionFactory transactionFactory;
private DataSource dataSource;
}
复制代码
非常清晰的映射关系, 一个环境里面包含了数据源以及事务工厂, 如果不是很理解事务工厂的用处, 可以先不用着急, 我们后面的文章会进行详细的分析, 在本小节, 只是为了告诉大家, 一个 mybatis 的配置文件, 最终其实是以 java 对象保存的, 而 xml 中的标签层级, 其实就是对应了对象中的属性层级
2.4、mapper 文件分析
对于 xxxMapper.xml 这些文件, mybatis 中通过 mapper 标签的 namespace + select / update 等标签中的 id 构成一个唯一标识(假设为 statementId), 每一个 sql 标签以 MappedStatement 的对象的形式保存在 Configuration 对象中, 在上面的 Configuration 中, 以 statementId -> MappedStatement 形成一个映射关系, 而 MappedStatement 中则存储了一个 select 等类型的标签中的所有内容, 比如 parameterType, resultType, resultMap, sql 等, 当我们在触发 sql 执行的时候, 即通过 statementId 找到对应的 MappedStatement, 取出里面的 sql 来执行, 然后利用 resulthandler 以及 resultType 或者 resultMap 等信息对结果集进行封装映射, 最后返回
在日常的开发中, 我们面向接口编程, 会定义一个个的 Mapper 接口类, 当执行这些接口方法的时候, 代理对象会利用接口的全路径类名 + 接口方法名构成一个 statementId, 进而获取到对应的 MappedStatement, 然后从中提取 sql, 执行 sql, 最后将结果集利用 MappedStatement 中的保存的 resultMap 等信息映射成对应的 java 对象
2.5、总结
mybatis-con.xml 这样的配置文件, 以 Configuration 对象存储, 表示整个 mybatis 的配置文件, 里面包含了一个环境(当前 mybatis 中对应的数据源以及事务工厂)以及所有的 Mapper 文件中一个个 sql 标签解析出来后的 MappedStatement 对象, xml 文件的层次对应了 java 对象中的属性层次, MappedStatement 中包含了一个 sql 标签需要执行的所有信息, 包括 sql、动态 sql 解析需要的数据以及通过 jdbc 查询到的结果集如果处理的信息
3、配置文件初始化源码分析
3.1、代码引入
public static void main(String[] args) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream( "mybatis-con.xml" ) );
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Object> objectList = sqlSession.selectList("com.fightzhong.mapper.CustomerMapper.selectRemarksById", 12602611);
}
复制代码
通过上面三行代码, 我们完成了 mybatis 的初始化, 以及执行了上面 mapper 中的一个 select 语句, 可以看到, 通过将 mybatis-con.xml 这个配置文件以流的形式读取, 然后利用这个文件流开始读取 mybatis 的配置, 我们可以先不用理会什么是 SqlSession 以及 SqlSessionFactory, 在 mybatis 源码分析的最后, 我
才会跟大家说明这两个东西是什么, 如果没有对底层依赖的组件有一个清晰的了解, 那么我们也不会深刻的了解到这两个类的真正作用! 不过 SqlSessionFactoryBuilder.build 方法却是我们需要进行分析的, 注意, 不要因为不知道 SqlSessionFactoryBuilder 和 SqlSession 是什么而感到烦恼!
3.2、build 方法开始构建 Configuration 对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
}
复制代码
可以看到, build 方法调用到最后, 就是创建了一个 XMLConfigBuilder, 然后调用它的 parse 方法创建了 Configuration 对象而已, build 方法的参数 inputStream 就是配置文件的文件流, environment 字段就是表示我们期望生效<environments>标签中的哪个环境
3.3、parse 方法开始构建 Configuration 对象
public Configuration parse() {
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
评论