Mybatis - 执行流程分析(源码分析)

前言

完成了一个User表的CRUD
了解了mybatis-config.xml的配置文件标签
学会了使用日志查看sql执行过程

现在,需要仔细分析Mybatis的执行流程


执行流程

其实,从我们前面的工具类大致可以看出一下步骤

Resources ——》InputStream(解析xml)——》SqlSessionFactoryBuilder().build(inputStream)——》SqlSessionFactory——》SqlSession——》动态代理Mapper接口

package com.learn.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //创建能执行sql命令的对象SqlSession
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

实际上,分析源码可以看出具体步骤:

  1. resources通过io获得mybatis-config.xml文件,解析成InputStream流

  2. new一个SqlSessionFactoryBuilder(建造者),调用build方法,把流中的配置文件数据读取到XMLConfigBuilder

  3. XMLConfigBuilder调用parse方法解析配置文件,它把配置文件中的各种标签属性全部解析获得数据,注入configuration对象存储数据

  4. 返回的configuration对象,回到SqlSessionFactoryBuilder的build方法,作为参数传入DefaultSqlSessionFactory并建造该对象,这个就是我们得到的SqlSessionFactory

  5. SqlSessionFactory.openSession()调用openSessionFromDataSource方法,该方法通过configuration获得配置文件中的环境标签,然后创建了Environment、TransactionFactory、Transaction、Executor对象,并返回了DefaultSqlSession

  6. 我们在测试类中使用的SqlSession就是这里返回的DefaultSqlSession,这里就进入到了测试类了

  7. sqlSession.getMapper(UserMapper.class)是把Mapper.class传入DefaultSqlSession,而DefaultSqlSession返回的是configuration.getMapper(type, this),configuration的方法又是返回mapperRegistry.getMapper(type, sqlSession);mapperRegistry类中使用动态代理帮mapper接口生成代理实现类

  8. 上面得到的代理对象就是结果,通过返回的代理对象执行mapper中的getUserList方法,其实走的是MapperProxy的invoke方法,这个方法最终返回mapperMethod.execute(sqlSession, args);

  9. 进入MapperMethod类中的execute方法是判断sql的执行类型,例如我们现在是select方法,通过判断进行sqlSession.select方法,又进入了DefaultSqlSession,这个方法最终执行的是executor.query

  10. 这里就有一定的顺序,先往CachingExecutor(二级缓存)查找,如果二级缓存没有数据,就进入delegate.query方法(一级缓存),如果一级缓存也没有,就查询数据库,并将数据库的数据放入一级缓存
    (一级缓存PerpetualCache其实是个HashMap)

  11. 再深追就是JDBC的内容了,最终得到了结果,返回结果集

这些都可以通过源码一步一步查询到

大致的流程图
在这里插入图片描述

后续开始源码分析Mybatis的执行流程


Resources

Resources类属于org.apache.ibatis.io包下,返回的是InputStream

在这里插入图片描述
可以看出,通过classLoaderWrapper类加载器的封装类,解析xml地址,来把mybatis-config.xml解析成InputStream流


SqlSessionFactoryBuilder

new一个SqlSessionFactoryBuilder,调用build方法,把前面的配置文件流放入

其实有很多build方法,可以对应reader流或者Properties配置文件、environment

在这里插入图片描述

指向3个参数的build方法,该方法内创建了XMLConfigBuilder对象
并调用了XMLConfigBuilder.parse方法解析配置文件流中的配置文件属性

在这里插入图片描述

最终调用参数是配置对象的build方法
传入配置对象即可返回DefaultSqlSessionFactory

在这里插入图片描述


XMLConfigBuilder

前面第一下是调用这个parse方法

在这里插入图片描述
evalNode是XPathParser类的方法
在这里插入图片描述

XPath是解析XML文件的解析方式,不清楚的可以看一下真的了解XML吗? - XML解析方式

我们清楚了通过这个方法解析了Mybatis-config.xml
然后调用parseConfiguration方法
在这里插入图片描述

看这一堆标签,不就是前面学的Mybatis核心配置文件的标签吗?
这个方法中包含了很多对应标签的方法,都是将标签内的属性注入属性对象configuration中
执行完这个方法后,把解析完xml并注入完属性的属性对象configuration返回

回到了SqlSessionFactoryBuilder,执行参数为配置文件的方法

在这里插入图片描述

返回DefaultSqlSessionFactory


SqlSession

DefaultSqlSessionFactory是工厂类,通过openSession创建了SqlSession(其实实现类是DefaultSqlSession)

在这里插入图片描述

前面分析了,在DefaultSqlSessionFactory中保存着配置属性对象

在这里插入图片描述

调用了openSessionFromDataSource方法(参数通过属性对象得到了执行器类型)

在这里插入图片描述

openSessionFromDataSource里创建了几个重要的对象:

  • Environment环境对象(配置文件里的Environments标签)
  • TransactionFactory事务工厂对象(创建事务对象)
  • Executor执行器(MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护)
  • 返回DefaultSqlSession

经过层层处理,最终返回了DefaultSqlSession(configuration, executor, autoCommit)

它带着属性对象configuration,执行器executor和boolean值判断是否自动提交
数据库的会话和事务控制放到了SqlSession对象中


getMapper

前面已经得到了DefaultSqlSession,接下来就是测试类的内容

在这里插入图片描述
sqlSession.getMapper方法,这是什么呢?
其实从它需要UserMapper的class文件就可以猜到使用的反射的方法,看看源码

我们查看DefaultSqlSession的getMapper方法:
在这里插入图片描述

它调用了configuration的getMapper方法(参数是mapper的class文件和sqlSession)

在这里插入图片描述

进行调用:
在这里插入图片描述

MapperProxyFactory、newInstance方法这不就是反射吗
HashMap,用于存储配置文件中的mappers标签的每个mapper接口

newInstance方法,帮mapper接口生成代理实现类
Proxy.newProxyInstance就是动态代理
不是很清楚的可以学习一下:设计模式(15)结构型模式 - 代理模式:静态、动态代理、Cglib代理
这种设计是代理模式的动态代理,当然还需要反射的知识

在这里插入图片描述

生成MapperProxy这个真正的事件处理对象


getUserList

获得了mapper接口的代理实现类,接下来就是进入具体的实现方法

在这里插入图片描述

动态代理就是代理对象的invoke方法代替Mapper接口的getUserList方法

真正进入的是MapperProxy的invoke方法

在这里插入图片描述

mapperMethod.execute方法判断sql的执行类型(把sqlSession作为参数)

在这里插入图片描述

根据不同的sql类型,执行不同的方法,我们现在是select方法,就查看select块:

在这里插入图片描述

又回到了DefaultSqlSession,执行select方法

在这里插入图片描述

调用了执行executor的query方法

在这里插入图片描述

executor执行器的结构是这样的:

在这里插入图片描述

走CachingExecutor的query方法,先从二级缓存中获取

在这里插入图片描述

这里有一个重要的对象BoundSql

getBoundSql负责获取绑定的sql命令,比如"SELECT * FROM xxx",赋值给BoundSql,创建cacheKey

然后进入另一个query方法
在这里插入图片描述

如果二级缓存不存在数据,delegate.query,即进入一级缓存查询数据
执行query操作的是SimplyExecutor代理来完成的,接着就进入到了SimplyExecutor的父类BaseExecutor的query方法中

在这里插入图片描述

localCache是一级缓存,localCache本质上是一个HashMap
在这里插入图片描述

在这里插入图片描述

如果是第一次执行查询操作(一级缓存也没有数据)

就调用queryFromDatabase方法去数据库查询

在这里插入图片描述

doQuery是抽象方法,所以进入子类SimplyExecutor,进行数据库查询

在这里插入图片描述

configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);根据既有的参数,创建StatementHandler对象来执行查询操作

SQL查询参数的设置prepareStatement(handler, ms.getStatementLog());,也就是咱们最开始传递进来的参数
handler.query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)

后续程序就是深入到SQL查询及结果集的设置中,然后是SQL查询操作和结果集的封装:可以看这篇文章SQL语句执行的完整流程


总结

在这里插入图片描述准备工作:

Resource对象把对应的XML文件加载,通过xmlConfigBulider解析,封装到Configuration对象,作为参数传递到SqlSessionFactory.build方法建造DefaultSqlSessionFactory,DefaultSqlSessionFactory的openSession创建DefaultSqlSession,带上了transaction和executor用于后续执行操作(数据库的会话和事务控制放到了SqlSession对象中

具体测试类:

DefaultSqlSession动态代理Mapper接口,生成代理对象MapperProxy,执行对应的方法,真正执行的是代理对象的invoke方法,该方法最终使用到executor执行器,先查看二级缓存是否存在数据,然后查看一级缓存,如果没有就通过StatementHandler进行数据库查询操作,query方法进行结果集封装

最终返回查询结果(list)

Mybatis中执行流程大致就是这样,源码还有很多没有分析的,如后续的sql查询、结果集处理,还有很多细节,留到以后再去分析

学海无涯苦作舟

都看到这了,点个赞呗(^_−)☆

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值