# 精尽 MyBatis 源码分析 —— 项目结构一览 # 1. 概述 本文主要分享 **MyBatis 的项目结构**。 希望通过本文能让胖友对 MyBatis 的整体项目有个简单的了解。 另外,[http://www.mybatis.org](http://www.mybatis.org/mybatis-3/zh/index.html) 提供了 MyBatis 非常不错的中文文档。如果胖友对 MyBatis 碰到一些使用上的问题,可以常常翻阅。 在拉取 MyBatis 项目后,我们会发现拆分了**好多**包。是不是内心一紧,产生了恐惧感?不要方,我们就是继续怼。 [![项目结构](03-mybatis-项目结构一览.assets/01.png)](http://static.iocoder.cn/images/MyBatis/2020_01_04/01.png)项目结构 # 2. 代码统计 这里先分享一个小技巧。笔者在开始源码学习时,会首先了解项目的代码量。 **第一种方式**,使用 [IDEA Statistic](https://plugins.jetbrains.com/plugin/4509-statistic) 插件,统计整体代码量。 [![Statistic 统计代码量](03-mybatis-项目结构一览.assets/02.png)](http://static.iocoder.cn/images/MyBatis/2020_01_04/02.png)Statistic 统计代码量 我们可以粗略的看到,总的代码量在 21441 行。还是在可接受的范围。 **第二种方式**,使用 [Shell 脚本命令逐个 Maven 模块统计](http://blog.csdn.net/yhhwatl/article/details/52623879) 。 一般情况下,笔者使用 `find . -name "*.java"|xargs cat|grep -v -e ^$ -e ^\s*\/\/.*$|wc -l` 。这个命令只过滤了**部分注释**,所以相比 [IDEA Statistic](https://plugins.jetbrains.com/plugin/4509-statistic) 会**偏多**。 如下是艿艿 `cd` 到每个包下,然后执行脚本统计后的结果: [![Statistic 统计代码量](03-mybatis-项目结构一览.assets/03.png)](http://static.iocoder.cn/images/MyBatis/2020_01_04/03.png)Statistic 统计代码量 # 3. 整体架构 > 老艿艿:本小节的内容,笔者会基于 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 的 [「1.4 MyBatis整体架构」](https://svip.iocoder.cn/MyBatis/intro/#) 小节来编写。 > > 这本书非常不错,笔者在死磕的源码的时候,帮助灰常大。 MyBatis 的整体架构分为三层: 1. 基础支持层 2. 核心处理层 3. 接口层 > [![整体架构](03-mybatis-项目结构一览.assets/04.png)](http://static.iocoder.cn/images/MyBatis/2020_01_04/04.png)整体架构 ## 3.1 基础支持层 基础支持层,包含整个 MyBatis 的基础模块,这些模块为核心处理层的功能提供了良好的支撑。 ### 3.1.1 反射模块 对应 `reflection` 包。 > Java 中的反射虽然功能强大,但对大多数开发人员来说,写出高质量的反射代码还是 有一定难度的。MyBatis 中专门提供了反射模块,该模块对 Java 原生的反射进行了良好的封装,提了更加**简洁易用的 API**,方便上层使调用,并且对**反射操作进行了一系列优化**,例如缓存了类的元数据,提高了反射操作的性能。 ### 3.1.2 类型模块 对应 `type` 包。 > ① MyBatis 为简化配置文件提供了**别名机制**,该机制是类型转换模块的主要功能之一。 > > ② 类型转换模块的另一个功能是**实现 JDBC 类型与 Java 类型之间**的转换,该功能在为 SQL 语句绑定实参以及映射查询结果集时都会涉及: > > - 在为 SQL 语句绑定实参时,会将数据由 Java 类型转换成 JDBC 类型。 > - 而在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型。 ### 3.1.3 日志模块 对应 `logging` 包。 > 无论在开发测试环境中,还是在线上生产环境中,日志在整个系统中的地位都是非常重要的。良好的日志功能可以帮助开发人员和测试人员快速定位 Bug 代码,也可以帮助运维人员快速定位性能瓶颈等问题。目前的 Java 世界中存在很多优秀的日志框架,例如 Log4j、 Log4j2、Slf4j 等。 > > MyBatis 作为一个设计优良的框架,除了提供详细的日志输出信息,还要能够集成多种日志框架,其日志模块的一个主要功能就是**集成第三方日志框架**。 ### 3.1.4 IO 模块 对应 `io` 包。 > 资源加载模块,主要是对类加载器进行封装,确定类加载器的使用顺序,并提供了加载类文件以及其他资源文件的功能 。 ### 3.1.5 解析器模块 对应 `parsing` 包。 > 解析器模块,主要提供了两个功能: > > - 一个功能,是对 [XPath](http://www.w3school.com.cn/xpath/index.asp) 进行封装,为 MyBatis 初始化时解析 `mybatis-config.xml` 配置文件以及映射配置文件提供支持。 > - 另一个功能,是为处理动态 SQL 语句中的占位符提供支持。 ### 3.1.6 数据源模块 对应 `datasource` 包。 > 数据源是实际开发中常用的组件之一。现在开源的数据源都提供了比较丰富的功能,例如,连接池功能、检测连接状态等,选择性能优秀的数据源组件对于提升 ORM 框架乃至整个应用的性能都是非常重要的。 > > MyBatis **自身提供了相应的数据源实现,当然 MyBatis 也提供了与第三方数据源集成的接口,这些功能都位于数据源模块之中**。 ### 3.1.7 事务模块 对应 `transaction` 包。 > MyBatis 对数据库中的事务进行了抽象,其自身提供了**相应的事务接口和简单实现**。 > > 在很多场景中,MyBatis 会与 Spring 框架集成,并由 **Spring 框架管理事务**。 ### 3.1.8 缓存模块 对应 `cache` 包。 > 在优化系统性能时,优化数据库性能是非常重要的一个环节,而添加缓存则是优化数据库时最有效的手段之一。正确、合理地使用缓存可以将一部分数据库请求拦截在缓存这一层。 > > MyBatis 中提供了**一级缓存和二级缓存**,而这两级缓存都是依赖于基础支持层中的缓 存模块实现的。这里需要读者注意的是,MyBatis 中自带的这两级缓存与 MyBatis 以及整个应用是运行在同一个 JVM 中的,共享同一块堆内存。如果这两级缓存中的数据量较大, 则可能影响系统中其他功能的运行,所以当需要缓存大量数据时,优先考虑使用 Redis、Memcache 等缓存产品。 ### 3.1.9 Binding 模块 对应 `binding` 包。 > 在调用 SqlSession 相应方法执行数据库操作时,需要指定映射文件中定义的 SQL 节点,如果出现拼写错误,我们只能在运行时才能发现相应的异常。为了尽早发现这种错误,MyBatis 通过 Binding 模块,将用户自定义的 Mapper 接口与映射配置文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。 > > 值得读者注意的是,开发人员无须编写自定义 Mapper 接口的实现,MyBatis 会自动为其创建动态代理对象。在有些场景中,自定义 Mapper 接口可以完全代替映射配置文件,但有的映射规则和 SQL 语句的定义还是写在映射配置文件中比较方便,例如动态 SQL 语句的定义。 ### 3.1.10 注解模块 对应 `annotations` 包。 随着 Java 注解的慢慢流行,MyBatis 提供了**注解**的方式,使得我们方便的在 Mapper 接口上编写简单的数据库 SQL 操作代码,而无需像之前一样,必须编写 SQL 在 XML 格式的 Mapper 文件中。虽然说,实际场景下,大家还是喜欢在 XML 格式的 Mapper 文件中编写响应的 SQL 操作。 ### 3.1.11 异常模块 对应 `exceptions` 包。 定义了 MyBatis 专有的 PersistenceException 和 TooManyResultsException 异常。 ## 3.2 核心处理层 在核心处理层中,实现了 MyBatis 的核心处理流程,其中包括 MyBatis 的**初始化**以及完成一次**数据库操作**的涉及的全部流程 。 ### 3.2.1 配置解析 对应 `builder` 和 `mapping` 模块。前者为配置**解析过程**,后者主要为 SQL 操作解析后的**映射**。 > 在 MyBatis 初始化过程中,会加载 `mybatis-config.xml` 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configuration 对象中。例如: > > - ``节点(即 ResultSet 的映射规则) 会被解析成 ResultMap 对象。 > - `` 节点(即属性映射)会被解析成 ResultMapping 对象。 > > 之后,利用该 Configuration 对象创建 SqlSessionFactory对象。待 MyBatis 初始化之后,开发人员可以通过初始化得到 SqlSessionFactory 创建 SqlSession 对象并完成数据库操作。 ### 3.2.2 SQL 解析 对应 `scripting` 模块。 > 拼凑 SQL 语句是一件烦琐且易出错的过程,为了将开发人员从这项枯燥无趣的工作中 解脱出来,MyBatis 实现**动态 SQL 语句**的功能,提供了多种动态 SQL语句对应的节点。例如`` 节点、`` 节点、`` 节点等 。通过这些节点的组合使用, 开发人 员可以写出几乎满足所有需求的动态 SQL 语句。 > > MyBatis 中的 `scripting` 模块,会根据用户传入的实参,解析映射文件中定义的动态 SQL 节点,并形成数据库可执行的 SQL 语句。之后会处理 SQL 语句中的占位符,绑定用户传入的实参。 ### 3.2.3 SQL 执行 对应 `executor` 和 `cursor` 模块。前者对应**执行器**,后者对应执行**结果的游标**。 > SQL 语句的执行涉及多个组件 ,其中比较重要的是 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 。 > > - **Executor** 主要负责维护一级缓存和二级缓存,并提供事务管理的相关操作,它会将数据库相关操作委托给 StatementHandler完成。 > - **StatementHandler** 首先通过 **ParameterHandler** 完成 SQL 语句的实参绑定,然后通过 `java.sql.Statement` 对象执行 SQL 语句并得到结果集,最后通过 **ResultSetHandler** 完成结果集的映射,得到结果对象并返回。 > > 整体过程如下图: > > [![整体过程](03-mybatis-项目结构一览.assets/05.png)](http://static.iocoder.cn/images/MyBatis/2020_01_04/05.png)整体过程 ### 3.2.4 插件层 对应 `plugin` 模块。 > Mybatis 自身的功能虽然强大,但是并不能完美切合所有的应用场景,因此 MyBatis 提供了插件接口,我们可以通过添加用户自定义插件的方式对 MyBatis 进行扩展。用户自定义插件也可以改变 Mybatis 的默认行为,例如,我们可以拦截 SQL 语句并对其进行重写。 > > 由于用户自定义插件会影响 MyBatis 的核心行为,在使用自定义插件之前,开发人员需要了解 MyBatis 内部的原理,这样才能编写出安全、高效的插件。 ## 3.3 接口层 对应 `session` 模块。 > 接口层相对简单,其核心是 SqlSession 接口,该接口中定义了 MyBatis 暴露给应用程序调用的 API,也就是上层应用与 MyBatis 交互的桥梁。接口层在接收到调用请求时,会调用核心处理层的相应模块来完成具体的数据库操作。 ## 3.4 其它层 这块,严格来说,不能叫做一个层。考虑到统一,就简单这么命名把。哈哈哈。 ### 3.4.1 JDBC 模块 对应 `jdbc` 包。 JDBC **单元测试**工具类。所以,不感兴趣的同学,已经可以忽略 1236 行代码了。 ### 3.4.2 Lang 模块 对应 `lang` 包。 看不懂具体用途,暂时误会。 ## 3.4 总结 总的来说,MyBatis 的代码,还是比较简单易懂。并且,看着包名,基本也能猜到大体的用途。在网络上找了一个总结的图,大家瞅瞅: > FROM [《mybatis 源码分析之整体架构分析》](http://zeng233.github.io/2015/08/20/2.1mybatis源码分析之前言/) > > [![模块图](03-mybatis-项目结构一览.assets/06.png)](http://static.iocoder.cn/images/MyBatis/2020_01_04/06.png)模块图 # 666. 彩蛋 有种,把 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 复制了一遍的感觉。不要打我的脸。 后续的文章,和 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 一样,按照**基础支持层 => 核心处理层 => 接口层**的顺序,逐步详细解析。虽然说,艿艿也想直接开始干核心处理层,但是考虑框架是自下而上,还是一点一点来。当然,热情的胖友,也可以从核心处理层开始看,然后看到不懂的地方,回到基础支持层。 如果对 MyBatis 不熟悉的胖友,推荐阅读下田小波的 [《MyBatis 源码分析系列文章导读》](https://www.tianxiaobo.com/2018/07/16/MyBatis-源码分析系列文章导读/) 。 同时,也推荐阅读无忌的 [《MyBatis 源码解读之源码结构》](https://my.oschina.net/wenjinglian/blog/1625437) 。