【源码解析】MyBatis 整体架构与源码解析

语言: CN / TW / HK

前言

:mailbox: 作者简介 :小明java问道之路,专注于研究计算机底层/Java/Liunx 内核,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计:mailbox: 

:trophy:CSDN 专家博主/Java 领域优质创作者、阿里云专家博主、华为云享专家、51CTO 专家博主:trophy:

:fire:如果此文还不错的话,还请:+1: 关注 、点赞 、收藏 三连支持:+1:一下博主~

本文导读

 【总】简述下 MyBatis 的三层架构以及其中各个模块的核心功能,从基础支撑层到核心处理层,再到暴露给调用的接口层。

【分】然后再深入剖析, MyBatis 中 n 种设计模式的使用;MyBatis 工作原理和运行流程 ;动态 SQL 语句解析;映射机制; 一二级缓存;

一、MyBatis 整体架构解析

MyBatis 架构图主要分三层。 基础支撑层: 负责最基础的功能支撑,包括 连接管理、事务管理、配置加载和缓存处理 这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。 数据处理层: 负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理   等。它主要的目的是根据调用的请求完成一次数据库操作。 API 接口层: 提供给外部使用的 接口 API ,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

下面对基础支撑层、数据处理层、API 接口层一一简述:

1、基础支撑层

MyBatis 基础支撑层  按照这些单一的能力可以划分为上图所示的九个基础模块。

第一个,类型转换模块。 在 mybatis-config.xml 配置文件中通过 <typeAliase> 标签为一个类定义别名,这里用到的“ 别名机制 ”就是由 MyBatis 基础支撑层中的类型转换模块实现的。类型转换模块还 实现了 MyBatis 中 JDBC 类型与 Java 类型之间的相互转换 ,在 SQL 模板绑定用户传入实参的场景中,类型转换模块会将 Java 类型数据转换成 JDBC 类型数据;在将 ResultSet 映射成结果对象的时候,类型转换模块会将 JDBC 类型数据转换成 Java 类型数据。

第二个,日志模块。 MyBatis 提供了日志模块来集成 Java 生态中的第三方日志框架,该模块目前可以集成 Log4j、Log4j2、slf4j 等优秀的日志框架。        第三个,反射工具模块。 MyBatis 的反射工具箱是在 Java 反射的基础之上进行的一层封装,为上层使用方提供更加灵活、方便的 API 接口。        第四个,Binding 模块。 MyBatis 无须编写 Mapper 接口的具体实现,而是利用 Binding 模块自动生成 Mapper 接口的动态代理对象。        第五个,数据源模块。 MyBatis 自身提供了数据源实现,也是 MyBatis 的默认实现。        第六个,缓存模块。MyBatis 提供在此提供一级缓存和二级缓存。        第七个,解析器模块。MyBatis 中有两大部分配置文件需要解析,一个是 mybatis-config.xml 配置文件,另一个是 Mapper.xml 配置文件。这两个文件都是由 MyBatis 的解析器模块进行解析的,其中主要是依赖 XPath 实现 XML 配置文件以及各类表达式的高效解析。        第八个,事务管理模块。 MyBatis 对数据库中的事务进行了一层简单的抽象,提供了简单易用的事务接口和实现。一般情况下,Java 项目都会集成 Spring,并由 Spring 框架管理事务。        第九个,资源加载模块。提供了 Resources 工具类,内部封装了 ClassLoaderWrapper 类的静态字段,Resources 提供的方法都是在 ClassLoaderWrapper 对象中实现的。加载配置文件用。

2、核心处理层

核心处理层是 MyBatis 核心实现所在,其中涉及就是 MyBatis 的初始化 以及 执行一条 SQL 语句的全流程

第一个,配置解析。 MyBatis 有三处可以添加配置信息的地方,分别是:mybatis-config.xml 配置文件、Mapper.xml 配置文件以及 Mapper 接口中的注解信息。在 MyBatis 初始化过程中,会加载这些配置信息,并将解析之后得到的配置对象保存到 Configuration 对象中。例如使用的 <resultMap> 标签(也就是自定义的查询结果集映射规则)会被解析成 ResultMap 对象。我们可以利用得到的 Configuration 对象创建 SqlSessionFactory 对象(也就是创建 SqlSession 对象的工厂对象),之后即可创建 SqlSession 对象执行数据库操作了。

第二个,SQL 解析与 scripting 模块。MyBatis 提供的动态 SQL 标签非常丰富,包括 <where> 标签、<if> 标签、<foreach> 标签、<set> 标签等。MyBatis 中的 scripting 模块负责动态生成 SQL 的核心模块。会根据运行时用户传入的实参,解析动态 SQL 中的标签,并形成 SQL 模板,然后处理 SQL 模板中的占位符,用运行时的实参填充占位符,得到数据库真正可执行的 SQL 语句。

第三个,SQL 执行。 在 MyBatis 中,要执行一条 SQL 语句,会涉及非常多的组件,比较核心的有: Executor、StatementHandler、ParameterHandler 和 ResultSetHandler。 其中, Executor 会调用事务管理模块实现事务的相关控制,同时会通过缓存模块管理一级缓存和二级缓存。SQL 语句的真正执行将会由 StatementHandler 实现。StatementHandler 会先依赖 ParameterHandler 进行 SQL 模板的实参绑定,然后由 java.sql.Statement 对象将 SQL 语句以及绑定好的实参传到数据库执行,从数据库中拿到 ResultSet,最后,由 ResultSetHandler 将 ResultSet 映射成 Java 对象返回给调用方,这就是 SQL 执行模块的核心。

3、接口层

接口层是 MyBatis 暴露给调用的接口集合,如 SqlSession 接口、SqlSessionFactory 接口等。其中,最核心的是 SqlSession 接口获取 Mapper 代理、执行 SQL 语句、控制事务开关等。

此时我们已经把 MyBatis 的三层架构以及其中各个模块的核心功能都阐述完毕,下面可以展开设计模式和 MyBatis 启动流程等。

二、MyBatis 中的设计模式的使用

MyBatis 的 整体架构 符合 外观模式(Facade Pattern) 的。 日志模块 是一个典型的使用适配器模式的场景 适配器模式(Adapter Pattern) 是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作;优先 加载日志 组件,以及 实现插件 时,使用 动态代理(Proxy Pattern) ,给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;数据源模块重点讲解数据源的创建和数据库连接池的源码分析; 数据源 创建比较复杂,对于复杂对象的创建,使用 工厂模式(Factory Pattern); 在 MyBatis 处理 动态 SQL 语句的时候,会将动态 SQL 标签解析为 SqlNode 对象,多个 SqlNode 对象就是通过 组合模式(Composite Pattern) 组成树形结构供上层使用的。          MyBatis 缓存模块 是一个经典的使用 装饰器模式(Decorator Pattern) 实现的模块,类图如下:Cache 接口是缓存模块的核心接口,定义了缓存的基本操作;PerpetualCache 在缓存模块中扮演 ConcreteComponent 角色,使用 HashMap 来实现 cache 的相关操作;BlockingCache:阻塞版本的缓存装饰器,保证只有一个线程到数据库去查找指定的 key 对应的数据, 缓存模块如下图

如下图,在 配置加载阶段 大量的使用了 建造者模式(Builder Pattern) 使用多个简单的对象一步一步构建成一个复杂的对象。BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供的通用的方法;XMLConfigBuilder: 主要负责解析 mybatis-config.xml;XMLMapperBuilder: 主要负责解析映射配置 Mapper.xml 文件;XMLStatementBuilder: 主要负责解析映射配置文件中的 SQL 节点;

SqlSessionFactory使 用 工厂模式(Factory Pattern) 创建 SqlSession , 其默认的实现类 DefaultSqlSessionFactory , 其中获取 SqlSession 的 核 心 方 法 openSessionFromDataSource(),在这个方法中从 configuration 中 获 取 的 TransactionFactory 是 典 型 的 策 略 模 式(Strategy Pattern) 的 应 用 。 运 行 期 ,TransactionFactory 接口的实现,是由配置文件配置决定的,可配置选项包括:JDBC、Managed, 可根据需求灵活的替换 TransactionFactory 的实现;         Executor 如下图: 定义了数据库操作最基本的方法;CacheingExecutor:使用 装饰器模式(Decorator Pattern) ,对真正提供数据库查询的 Executor 增强了二级缓存的能力 ; 二级缓存 初始化位置 :DefaultSqlSessionFactory.openSessionFromDataSource();BaseExecutor:抽象类,实现了 executor 接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery 等方法;BatchExecutor: 批量执行所有更新语句,基于 jdbc 的 batch 操作实现批处理;SimpleExecutor: 默认执行器,每次执行都会创建一个 statement,用完后关闭。;ReuseExecutor: 可重用执行器,将 statement 存入 map 中,操作 map 中的 statement 而不会重复创建 statement;         BaseExecutor 、BaseStatementHandler 模板模式(Template Pattern) ;BaseExecutor 执行器抽象类,实现了 executor 接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery 等方法;

三、MyBatis 工作原理(运行流程)

【总】可以把 MyBatis 的运行流程分为三大阶段: 1. 初始化阶段: 读取 XML 配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作; 2. 代理封装阶段: 封装 iBatis 的编程模型,使用 mapper 接口开发的初始化工作; 3. 数据访问阶段: 通过 SqlSession 完成 SQL 的解析,参数的映射、SQL 的执行、结果的解析过程;

(1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。

(2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

(3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

(4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。

(5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

(6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

(7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

(8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

总结

简述下 MyBatis 的三层架构以及其中各个模块的核心功能,从基础支撑层到核心处理层,再到暴露给调用的接口层。然后再深入剖析,MyBatis 中 n 种设计模式的使用;MyBatis 工作原理和运行流程 ;动态 SQL 语句解析;映射机制; 一二级缓存;

划线

评论

复制