Mr. Cappuccino的第53杯咖啡——Mybatis源码分析

Mybatis源码分析

    • Mybatis源码分析入口
    • 1. 读取配置文件
      • 总结
    • 2. 解析配置文件
      • 核心代码(一)
      • 核心代码(二)
        • 分析parse()方法
        • 分析build()方法
      • 总结
    • 3. 获取SqlSession
      • 总结
    • 4. 获取mapper代理对象
      • 总结
    • 5. 使用mapper代理对象执行Sql语句
      • 二级缓存
      • 一级缓存
      • 总结

Mybatis源码分析入口

本文将根据下面这段代码进行源码分析

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserEntity> list = mapper.listUser();
System.out.println(list);
sqlSession.close();

1. 读取配置文件

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

ClassLoaderWrapper.java

在这里插入图片描述


总结

从入口一路点击进去可以发现底层是通过调用java.lang.ClassLoader#getResourceAsStream方法来读取resources目录下的mybatis-config.xml文件,并得到InputStream对象

2. 解析配置文件

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactoryBuilder.java

在这里插入图片描述

在这里插入图片描述


核心代码(一)

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

XMLConfigBuilder.java

在这里插入图片描述

XPathParser.java

在这里插入图片描述

可以发现底层是将InputStream对象转换成Document对象,并将Document对象保存至当前类(XPathParser)的document属性中

继续回到上一层,点击进入this()方法

XMLConfigBuilder.java

在这里插入图片描述

可以发现this()方法主要是在进行部分属性的初始化,并将XPathParser对象保存至当前类(XMLConfigBuilder)的parser属性中。
关键点:初始化了父类的configuration属性。


核心代码(二)

return build(parser.parse());

分析parse()方法


XMLConfigBuilder.java

在这里插入图片描述

  1. 根据Document对象获取节点为configuration的配置信息,并转换成XNode对象

XPathParser.java

在这里插入图片描述

  1. 将XNode对象解析成Configuration对象

XMLConfigBuilder.java

在这里插入图片描述

XMLConfigBuilder.java

在这里插入图片描述


点击进入addMappers(mapperPackage)方法


Configuration.java

在这里插入图片描述

MapperRegistry.java

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

从这里可以发现parser.parse()主要是在解析配置文件(mybatis-config.xml),具体过程是根据Document对象获取节点为configuration的配置信息,并转换成XNode对象再解析各个节点,重点部分是mappers节点的解析。
在解析mappers节点的代码中可以发现如果是使用package或class注册mapper可以直接注册mapper接口对象,如果是使用url或者resource注册mapper则需要先解析mapper.xml映射文件后并通过namespace找到所绑定的接口对象再进行注册。
mapper的注册是通过MapperRegistry对象完成的,而MapperRegistry则是Configuration对象里面的一个属性,也就是说所有的配置解析完成后都存放在Configuration对象中。
parser.parse()最终返回Configuration对象。

分析build()方法


SqlSessionFactoryBuilder.java

在这里插入图片描述

DefaultSqlSessionFactory.java

在这里插入图片描述

从这里可以发现SqlSessionFactoryBuilder将得到的Configuration对象建造成DefaultSqlSessionFactory对象,也就是SqlSessionFactory对象。


总结

SqlSessionFactoryBuilder先是通过XMLConfigBuilder解析配置文件并将解析得到的配置装载到Configuration对象中,再将Configuration建造成DefaultSqlSessionFactory对象。

这里采用了建造者设计模式
BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供一些通用的方法;
XMLConfigBuilder:主要负责解析mybatis-config.xml文件;
XMLMapperBuilder:主要负责解析mapper.xml文件;
XMLStatementBuilder:主要负责解析映射文件中的SQL节点;

Configuration对象核心属性释义:

  1. MapperRegistry:mapper接口动态代理工厂类的注册中心;
  2. ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id、result等子元素;
  3. MappedStatement:用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性;
  4. SqlSource:用于创建BoundSql,mapper.xml文件中的sql语句会被解析成BoundSql对象,经过解析BoundSql包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;

3. 获取SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();

DefaultSqlSessionFactory.java

在这里插入图片描述

Configuration.java

在这里插入图片描述
在这里插入图片描述

从这里可以看到如果没有设置执行器类型,则会默认使用简单执行器类型

ExecutorType.java

在这里插入图片描述

上面枚举类中的三种执行器类型均可通过openSession()传参设置


点击进入openSessionFromDataSource()方法


DefaultSqlSessionFactory.java

在这里插入图片描述

openSessionFromDataSource()方法有三个入参:ExecutorType execType(执行器类型)、TransactionIsolationLevel level(事务隔离级别)、boolean autoCommit(是否自动提交)

  1. 获取TransactionFactory

DefaultSqlSessionFactory.java

在这里插入图片描述

TransactionFactory有两种:JdbcTransactionFactory,ManagedTransactionFactory
通过mybatis-config.xml文件进行配置

在这里插入图片描述

<transactionManager type="JDBC"/>

这里配置的是JdbcTransactionFactory

  1. 获取Transaction

JdbcTransactionFactory.java

在这里插入图片描述

JdbcTransaction.java

在这里插入图片描述

  1. 根据Transaction和执行器类型获取执行器(核心代码)

Configuration.java

在这里插入图片描述

CachingExecutor.java

在这里插入图片描述

Mybatis默认使用的执行器是SimpleExecutor,SimpleExecutor的父类是BaseExecutor,BaseExecutor下一共有三个子类也就是三种执行器:BatchExecutor、SimpleExecutor、ReuseExecutor,这三种执行器均可通过传值设置。

cacheEnabled默认值为true,说明Mybatis默认会使用CachingExecutor。进入CachingExecutor类可以发现,CachingExecutor是在上面三种执行器(BaseExecutor)的基础上做了一层包装(装饰器设计模式),先调用CachingExecutor再调用BaseExecutor,是对BaseExecutor类的增强。

cacheEnabled可以通过mybatis-config.xml文件进行配置

<settings><!-- 是否开启二级缓存 --><setting name="cacheEnabled" value="false"/>
</settings>

BaseExecutor是一级缓存(默认开启),默认使用SimpleExecutor,CachingExecutor是二级缓存(默认开启,但还需要做一些额外的配置才能生效)

  1. 生成DefaultSqlSession

在这里插入图片描述

将Configuration、Executor、autoCommit等信息包装成DefaultSqlSession对象,并且返回该对象


总结

openSession()是SqlSessionFactory接口中的一个重载方法,可以配置执行器类型、事务隔离级别、是否自动提交等参数,Configuration负责判断当前使用的执行器(Executor),DefaultSqlSessionFactory最后将Configuration、Executor、autoCommit等信息包装成DefaultSqlSession对象并返回。

这里采用了装饰器设计模式

BaseExecutor是一级缓存(默认开启),BaseExecutor是BatchExecutor、SimpleExecutor、ReuseExecutor三种执行器的父类。

  1. SimpleExecutor:默认的Executor,每个SQL执行的时候都会创建新的Statement;
  2. ReuseExecutor:相同的SQL会重复使用Statement;
  3. BatchExecutor:用于批处理的Executor;

CachingExecutor是二级缓存(默认开启,但还需要做一些额外的配置才能生效)
CachingExecutor:可缓存数据的Executor,用装饰器模式包装了其它的执行器(如BaseExecutor下的三种执行器)

4. 获取mapper代理对象

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

DefaultSqlSession.java

在这里插入图片描述

Configuration.java

在这里插入图片描述

MapperRegistry.java

在这里插入图片描述

之前已经对mapper接口进行了注册,这里通过mapper接口类型获取对应的动态代理工厂类(MapperProxyFactory),动态代理工厂类使用JDK动态代理技术生成mapper代理对象并返回该对象。

MapperProxyFactory.java

在这里插入图片描述

MapperProxy.java

在这里插入图片描述

JDK动态代理技术主要用于拦截和修改方法的调用,在使用mapper代理对象调用mapper接口中的方法时MapperProxy中的invoke方法也会被执行。

总结

根据mapper接口类型从MapperRegistry中获取对应的动态代理工厂类(MapperProxyFactory),动态代理工厂类使用JDK动态代理技术生成mapper代理对象并返回该对象。在使用mapper代理对象调用方法时底层会走MapperProxy中的invoke方法。

这里采用了JDK动态代理设计模式

MapperRegistry:mapper接口动态代理工厂类的注册中心;
MapperProxyFactory:用于生成动态代理的实例对象;
MapperProxy:动态代理回调类;

5. 使用mapper代理对象执行Sql语句

List<UserEntity> list = mapper.listUser();

在这里插入图片描述

在这里插入图片描述


MapperProxy.java

在这里插入图片描述

在这里插入图片描述

核心代码

mapperMethod.execute(sqlSession, args);

MapperMethod.java

在这里插入图片描述

因为执行的SQL为select,返回值类型为List集合,所以会走executeForMany()方法

在这里插入图片描述

DefaultSqlSession.java

在这里插入图片描述

这个方法是不是很熟悉,没错,这就是在基于XML方式-原生方式开发用到的方法

List<UserEntity> list = sqlSession.selectList("com.mybatis.mapper.UserMapper.listUser", UserEntity.class);

DefaultSqlSession.java

在这里插入图片描述

在这里插入图片描述

二级缓存

如果开启了二级缓存则会使用CachingExecutor

CachingExecutor.java

在这里插入图片描述

  1. 获取SQL语句

在这里插入图片描述

  1. 创建缓存key

在这里插入图片描述

  1. 执行查询逻辑

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

SimpleExecutor中没有query方法,默认走父类(BaseExecutor)


一级缓存

BaseExecutor.java

在这里插入图片描述
在这里插入图片描述

SimpleExecutor.java

在这里插入图片描述

  1. 初始化RoutingStatementHandler对象

Configuration.java

在这里插入图片描述

RoutingStatementHandler.java

在这里插入图片描述

  1. 生成Statement对象

SimpleExecutor.java

在这里插入图片描述

2.1. 获取Connection

BaseExecutor.java

在这里插入图片描述

JdbcTransaction.java

在这里插入图片描述

2.2. 根据不同的StatementHandler创建Statement对象

RoutingStatementHandler.java

在这里插入图片描述

BaseStatementHandler.java

在这里插入图片描述

Mybatis默认采用PreparedStatementHandler处理器

PreparedStatementHandler.java

在这里插入图片描述

2.3. 使用ParameterHandler处理占位符参数

RoutingStatementHandler.java

在这里插入图片描述

PreparedStatementHandler.java

在这里插入图片描述

DefaultParameterHandler.java

在这里插入图片描述

  1. 执行查询逻辑

RoutingStatementHandler.java

在这里插入图片描述

PreparedStatementHandler.java

在这里插入图片描述

DefaultResultSetHandler.java

在这里插入图片描述


总结

在使用代理对象调用方法时,底层会走MapperProxy中的invoke方法,在执行查询语句时,默认会先从二级缓存(CachingExecutor)中读取数据,如果存在则直接返回,不存在则继续查询一级缓存,如果一级缓存(BaseExecutor)中存在则直接返回,不存在则继续查询数据库,在查询数据库时,总体上使用StatementHandler对象和JDBC进行交互,整个查询流程先是使用ParameterHandler对SQL语句的入参进行处理,待SQL语句被执行完后得到结果集,再使用ResultSetHandler对结果集进行处理并返回。

四大核心接口对象

  1. Executor(执行器):负责整个SQL执行过程的总体控制;
  2. StatementHandler(语句处理器):负责和JDBC层的具体交互;
  3. ParameterHandler(参数处理器):负责PreparedStatement入参的具体设置;
  4. ResultSetHandler(结果集处理器):负责将JDBC查询的结果映射为Java对象;

StatementHandler

  1. RoutingStatementHandler:根据StatementType路由到不同的StatementHandler对象;
  2. SimpleStatementHandler:管理Statement对象并向数据库中推送不需要预编译的SQL语句;
  3. PreparedStatementHandler:管理Statement对象并向数据库中推送需要预编译的SQL语句;
  4. CallableStatementHandler:管理Statement对象并调用数据库中的存储过程;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/22036.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL操作命令详解:增删改查

文章目录 一、CRUD1.1 数据库操作1.2 表操作1.2.1 五大约束1.2.2 创建表1.2.3 修改表1.2.3 删除表1.2.4 表数据的增删改查1.2.5 去重方式 二、高级查询2.1 基础查询2.2 条件查询2.3 范围查询2.4 判空查询2.5 模糊查询2.6 分页查询2.7 查询后排序2.8 聚合查询2.9 分组查询2.10 联…

【移动机器人运动规划】02 —— 基于采样的规划算法

文章目录 前言相关代码整理:相关文章&#xff1a; 基本概念概率路线图&#xff08;Probabilistic Road Map&#xff09;基本流程预处理阶段查询阶段 优缺点&#xff08;pros&cons&#xff09;一些改进算法Lazy collision-checking Rapidly-exploring Random Tree算法伪代码…

数据结构 10-排序4 统计工龄 桶排序/计数排序(C语言)

给定公司名员工的工龄&#xff0c;要求按工龄增序输出每个工龄段有多少员工。 输入格式: 输入首先给出正整数&#xff08;≤&#xff09;&#xff0c;即员工总人数&#xff1b;随后给出个整数&#xff0c;即每个员工的工龄&#xff0c;范围在[0, 50]。 输出格式: 按工龄的递…

【Jmeter】配置不同业务请求比例,应对综合场景压测

目录 前言 Jmeter5.0新特性 核心改进 其他变化 资料获取方法 前言 Jmeter 5.0这次的核心改进是在许多地方改进了对 Rest 的支持&#xff0c;此外还有调试功能、录制功能的增强、报告的改进等。 我也是因为迁移到了Mac&#xff0c;准备在Mac上安装Jmeter的时候发现它已经…

Keil MDK环境下FreeModebus移植踩坑记录

Keil MDK环境下FreeModebus移植踩坑记录 文章目录 Keil MDK环境下FreeModebus移植踩坑记录armcc (arm compiler v5)环境实验一&#xff1a;实验二&#xff1a; armclang (arm compiler v6)环境实验一&#xff1a;实验二&#xff1a;实验三&#xff1a;实验四 总结 armcc (arm c…

安防视频汇聚平台EasyCVR视频广场面包屑侧边栏支持拖拽操作

智能视频监控平台EasyCVR能在复杂的网络环境中&#xff0c;将海量设备实现集中统一接入与汇聚管理&#xff0c;实现视频的处理与分发、录像与存储、按需调阅、平台级联等。 TSINGSEE青犀视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协…

详聊API接口?淘宝API接口在ERP系统中扮演者什么角色?

什么是API&#xff1f; API全称应用程序编程接口&#xff08;Application Programming Interface&#xff09;&#xff0c;是一组用于访问某个软件或硬件的协议、规则和工具集合。电商API就是各大电商平台提供给开发者访问平台数据的接口。目前&#xff0c;主流电商平台如淘宝…

软件安全测试和渗透测试的区别在哪?安全测试报告有什么作用?

软件安全测试和渗透测试在软件开发过程中扮演着不同的角色&#xff0c;同时也有不同的特点和目标。了解这些区别对于软件开发和测试人员来说非常重要。本文将介绍软件安全测试和渗透测试的区别&#xff0c;以及安全测试报告在软件开发和测试过程中的作用。 一、 软件安全测试和…

【JavaEE初阶】Servlet(四) Cookie Session

文章目录 1. Cookie && Session1.1 Cookie && Session1.2 Servlet会话管理操作 1. Cookie && Session 1.1 Cookie && Session Cookie是什么? Cookie是浏览器提供的持久化存储数据的机制.Cookie从哪里来? Cookie从服务器返回给浏览器. 服务…

IntelliJ IDEA 2023.2社区版插件汇总

参考插件帝&#xff1a;https://gitee.com/zhengqingya/java-developer-document 突发小技巧&#xff1a;使用插件时要注意插件的版本兼容性&#xff0c;并根据自己的实际需求选择合适的插件。同时&#xff0c;不要过度依赖插件&#xff0c;保持简洁和高效的开发环境才是最重要…

JdbcTemplate

目录 1、简介 2、开发步骤 2.1、导入坐标 2.2、创建表和类 2.3、创建JdbcTemplate对象 2.4、执行数据库操作 3、解耦 4、增删改查 ⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹…

AlmediaDev Style Controls Crack

AlmediaDev Style Controls Crack StyleControls是一个稳定、强大的包(超过100个组件)&#xff0c;它使用经典绘图、系统主题、GDI和VCL样式。该软件包包含扩展标准VCL控件的独特解决方案&#xff0c;还包含许多独特的高级控件&#xff0c;用于创建具有Fluent UI模糊背景的现代…

Windows下QT Creator安装MinGW 32bit编译器

前言 注&#xff1a;本作者是基于FFmpeg开发需要&#xff0c;故在Windows下QT Creator中安装MinGW 32bit编译器&#xff01;其它型号编译器参照此文章基本可以实现&#xff01; 一、下载需要的编译器 1、下载链接 链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/…

C++ - 模版进阶 - array

简介 之前对模版的进行了初步了解和使用&#xff0c;可查看博客&#xff1a;C 初始模板_c模板初始化_chihiro1122的博客-CSDN博客 其实模版除了是一类算法&#xff0c;或者自定义类型的 套用&#xff0c;还有其他功能&#xff0c;和其他的更高阶的使用方法。 之前在实现 各种 …

集成学习算法是什么?如何理解集成学习?

什么是集成学习&#xff1f; 集成学习通过建立几个模型来解决单一预测问题。它的工作原理是生成多个分类器/模型&#xff0c;各自独立地学习和作出预测。这些预测最后结合成组合预测&#xff0c;因此优于任何一个单分类的做出预测。 机器学习的两个核心任务 任务一&#xff1…

嵌入式:C高级 Day3

一、整理思维导图 二、判断家目录下&#xff0c;普通文件的个数和目录文件的个数 三、输入一个文件名&#xff0c;判断是否为shell脚本文件&#xff0c;如果是脚本文件&#xff0c;判断是否有可执行权限&#xff0c;如果有可执行权限&#xff0c;运行文件&#xff0c;如果没有可…

ARM 常见汇编指令学习 9 - 缓存管理指令 DC 与 IC

文章目录 ARM64 DC 与 IC 指令 上篇文章&#xff1a;ARM 常见汇编指令学习 8 - dsb sy 指令及 dsb 参数介绍 ARM64 DC 与 IC 指令 AArch64指令集中有两条关于缓存维护&#xff08;cache maintenance&#xff09;的指令&#xff0c;分别是IC和DC。 IC 是用于指令缓存操作&…

4.msf辅助模块

目录 1 在虚拟机中设置与外部相同的网段 2 当前内网中的可用IP arp_sweep 3 搜索指定IP的TCP端口信息 portscan/tcp 4 扫描http服务的路由 http/dir_scanner 5 SSH密码爆破 ssh/ssh_login 1 在虚拟机中设置与外部相同的网段 我真实机的地址的网段是192.168.0 我虚拟…

前端:地图篇(一)

1、前言 在很多的出行程序中&#xff0c;都会使用到地图这一个功能&#xff0c;在实际的开发中我们也不会去开发一个自己的地图模型。如果自己开发一个地图模型&#xff0c;那么需要投入的成本、人力都是非常巨大的。所以我们很多网站和APP中使用的都是第三方的接口和JS&#…

MPLS(下)

LDP --- 标签分发协议 --- 主要应用在MPLS的控制层面 MPLS控制层面需要完成的工作主要就是分配标签和传递标签。分配标签的前提是本地路由表中得先存在标签&#xff0c;传递标签的前提也是得先具备路由基础。所以&#xff0c;LDP想要正常工作&#xff0c;则需要IGP作为基础。 …