GitHub地址
源码解析地址
https://github.com/erlieStar/mybatis-3
debug源码用项目
https://github.com/erlieStar/mybatis-examples
介绍
Mybatis的核心流程主要分为两大阶段
- 初始化阶段:读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化工作
- 动态代理阶段:当执行SQL时,通过动态代理的类,完成参数映射,SQL执行,结果映射
mybatis的初始化阶段代码还是非常清晰的,基本上就是对配置文件中的各种属性进行解析,然后将值保存到Configuration对象中。配置文件分为两部分,1.mybatis配置文件,2.mapper映射文件。
因为配置文件的解析还是比较复杂的,所以mybatis用了建造者模式,将对象与对象的创建过程进行了解耦。建造者模式UML图如下
主要有如下4个角色:
- 建造者(Builder)接口:定义对象各部分的行为
- 具体建造者(ConcreteBuilder):一般来说有2种类型的方法,1.建造方法,如buildPart1(),2.获取构建好的产品对象的方法,如getProduct()方法
- 导演(Director):通过调用具体建造者,创建需要的产品
- 产品(Product):用户需要使用的复杂对象
XMLConfigBuilder:主要负责解析mybatis-config.xml
XMLMapperBuilder:主要负责解析映射配置文件中的cache-ref,cache,parameterMap,resultMap,sql节点
XMLStatementBuilder:主要负责解析映射配置文件中的select,insert,update,delete这四类节点
示例工程如下
public class Part4Main {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config4.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 这一句执行完后,Mybatis环境就初始化完了
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = null;
try {
sqlSession = sqlSessionFactory.openSession(true);
AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
List<Author> authorList = authorMapper.selectAuthortList();
authorList.forEach(item -> {
System.out.println(item);
});
} catch(Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
看源码的时候你从这一行开始看,看到最后返回SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
各种解析规则我就不详细介绍了,自己对着配置文件Debug几遍就知道了。解析过程的时序图如下
一直追发现最后返回的SqlSessionFactory为DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
可以看到最后解析完生成了一个Configuration对象,用来保存各种属性,并设置到DefaultSqlSessionFactory中,来研究一下Configuration对象到底存了哪些东西
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
一些boolean值可以在<setting>节点中配置,直接映射到Configuration对象的属性,可以到官网上看一下每个属性的作用
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
说几个比较重要的属性mappedStatements
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
初始化完毕已经给每个接口生成了一个动态代理工厂类,这个类会在后续动态代理的时候用到
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
映射配置文件中的SQL节点会被解析成MappedStatement对象,其中的SQL语句被解析成SqlSource对象
可以看到一个SQL放了2次,key分别为方法名字和接口全限定名+方法名字放一次。
一些简单的SQL在初始化的时候,就已经被解析为StaticSqlSource(可能会含有?的sql文本),这样在执行的时候只要替换一下相应的参数就能执行
<delete id="deleteById" parameterType="int">
delete from book where id = #{id}
</delete>
我用这个例子跑后面代理的过程,不用select节点是为了让你迅速明白整个流程,而select节点的映射代码还是比较复杂的
而其他一些复杂的SQL则没有,后续我再补充
参考博客
[1]https://www.jianshu.com/p/7bc6d3b7fb45