Mybatis源码解析之流程解析:初始化阶段

GitHub地址

源码解析地址
https://github.com/erlieStar/mybatis-3

debug源码用项目
https://github.com/erlieStar/mybatis-examples

介绍

Mybatis的核心流程主要分为两大阶段

  1. 初始化阶段:读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化工作
  2. 动态代理阶段:当执行SQL时,通过动态代理的类,完成参数映射,SQL执行,结果映射

mybatis的初始化阶段代码还是非常清晰的,基本上就是对配置文件中的各种属性进行解析,然后将值保存到Configuration对象中。配置文件分为两部分,1.mybatis配置文件,2.mapper映射文件。

因为配置文件的解析还是比较复杂的,所以mybatis用了建造者模式,将对象与对象的创建过程进行了解耦。建造者模式UML图如下

在这里插入图片描述
主要有如下4个角色:

  1. 建造者(Builder)接口:定义对象各部分的行为
  2. 具体建造者(ConcreteBuilder):一般来说有2种类型的方法,1.建造方法,如buildPart1(),2.获取构建好的产品对象的方法,如getProduct()方法
  3. 导演(Director):通过调用具体建造者,创建需要的产品
  4. 产品(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

扫描二维码关注公众号,回复: 10042868 查看本文章
  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

欢迎关注

在这里插入图片描述

发布了385 篇原创文章 · 获赞 1471 · 访问量 90万+

猜你喜欢

转载自blog.csdn.net/zzti_erlie/article/details/104416601