文本阅读大约需要11分钟
作者:鲁班大叔
概要
前言
整体架构
学习重点
学习路径
总结

在MyBatis 3.5.1这个版中有968个类、2770个字段、8422个方法、42504行代码、186428个指令码。其源码体量只有Spring 的1/5,也是Hibernate的1/5。但在功能上这三个框架并无高低之分,但从源码轻量级来讲,MyBatis无疑是最成功的。

这其中原因在于作者Clinton Begin 极简主义风格,以及恰到好处的设计,不去盲目追求可扩展性。比如:在MyBatis代码中很少有注释,但你并不会因此看不懂,而Spring 源码中随处可见大量的注释,反而还看不懂,这就是极简主义的好处。

此外MyBatis也是尽可能的不增加新概念。比如:它的插件机制和Spring AOP功能都是一样的。MyBatis的整个实现不到350行代码,涉及概念有拦截器、执行器两种。而Spring AOP 用一整个模块实现,代码有5700行。涉及概念有通知、连接点、切入点、切面、引入、目标、代理、织入等。这里我不评判这些概念有无必要,但它增加了用户理解难度,这点是确定的。

综上所述,如果你想完整的吃透一个框架源码,MyBatis绝对是最省力气的。更重要的是它优秀的设计也非常值得我们去学习。那我们应该如何开始呢?首先得了解它的宏观架构。

MyBatis可分为三层,接口层、数据处理层、扩展与支撑层。

指用于操作数据库的增删改查接口,如SqlSession以及用户自定义的Mapper接口,都属于接口层组件。通常我们对MyBatis源码学习是从接口层开始的,但这并不是一个好方法,至于原因会在学习路径中讲到。

SQL执行流程实现,内容包括动态SQL语句构建、参数映射处理、结果映射集处理。他是整个MyBatis源码的核心,也是我们学习的重点和难点。

指对主流程功能上的扩展与支撑。
扩展:对主流程,在易用性、性能上进行扩展。如SqlSession与Mappr接口就是易用性扩展。缓存与懒加载则是执行性能的扩展。
支撑:主程所依赖的工具,如数据源、反射工具、日志管理、配置构建器、脚本执行器等。

数据处理层是MyBatis核心与重点,我们学MyBatis源码其实就是学它,搞定它MyBatis源码就掌握了80%,当然它的难度也是最大的。为方便理解,我把它分成了执行流程与映射配置两部分。
执行流程:体现SQL执行过程中的每个步骤。
映射配置:SQL执行前后的动态SQL解析、参数映射、结果集映射。

一个完整SQL执行流程,至少包含以下节点:
执行器:处理一至多个SQL、以及、缓存、事务、批处理等。
Sql处理器 :用于声明Statement、参数设置、执行、以及结果集处理。
参数处理器:基于映射进行具体的参数赋值处理。
结果集处理器:基于映射进行具体的结果集处理。

(注:图中绿色字体表示,表示交给下个组件实现)
每个节点都会完成SQL执行过程中特定任务,越到后面,其负责的事情就会越具体。

我们可以把 执行器理解成一个“指挥官”,它组织一至多次的SQL进攻,并把握进攻的节奏和方式,但自己不参与实际的战斗。
缓存:佯攻,虚张声势并不会真正对数据库发起进攻。
批处理:部队集结在一起,然后一起进攻
事务:决定何时结束战斗
SQL执行:真正发起进攻(交给小队去执行)

Sql处理器理解成一个“队长”,它带队负责一次具体的SQL进攻。不仅自己要亲自上阵,还要分配任务给队员。
SQL声明:申请作战武器
参数处理:炮弹上膛,分配任务给队员
执行:开火发射
结果集处理:清理战场,分配任务给队员
最后参数处理器、结果集处理器、类型处理器都是负责具体局部任务的士兵。

MyBatis源码中映射配置特指以下三件事情:
动态SQL:基于配置的脚本,以及参数生成Sql字符串。
参数映射:转换JAVA参数,并设置成SQL参数
结果集映射解析:指定数据结果集,转换成JAVA对象。有1对1转换、1对多转换,还包括映射循环依赖等解决方案。它是MyBatis当中最难的知识点。
看到这里,你可能会奇怪,结果集映射,与结果集处理不是一回事吗?为啥要分开讲?确实是在干同一件事情。但关注点不一样,结果集处理关注流程,关注其在Sql流程当中的位置,上下承接了哪些组件?而勿略具体的处理细节。结果集映射就是具体的1对1、1对多的处理细节。
分开讲的目的,是帮助读者你能循序渐近,有层次的去掌握MyBatis,而不是一开始就陷入无休止的细节当中。参数映射也是同理的。

关于MyBatis源码学习路径,供我们选择的有三条:
从上往下,按接口层、数据处理层、基础支撑层的顺序学习。
从下往上,反过来,先把底层依赖掌握,在去掌握上层应用。
从中间往两头,即直接学习中间数据处理流程,然后在去学习其中牵扯出的技术。

我们应该选择第三种。因为源码的体量非常大,必须先抓核心。
而第一种方式很难抓住核心,因为接口层是抽象的,其实例也要被代理、要被适配之后,才会进入核心。比如SqlSession在走SQL执行流程前,要经过系列的参数转换,或方法适配。
同样也不适合用第三种,即使它是基础很重要,但因为它都是为数据处理层服务,如果脱离应用场景去学习,会加大理解难度。给人一种不能落地的感觉。
实践证明,第三种是最可靠的。直接去学习核心流程,在核心流程学习过程中,把相关依赖搞定。最后做为流程的延伸去讲解接口层。
最后确定学习路径如下:
执行流程
映射配置
扩展支撑


学MyBatis源码之前一定要分解目标,并适时检查自己是否完成目标,这样才能持续学习下去。我们经常说源码学了就忘记,原因就是没有学习目标去检查,导致学了半桶水也不知道。这种不完善的知识体系是最容易忘记的。以下就是我们要给自设定的学习目标:

MyBatis源码学习的第一个目标就是要搞定四个组件所组成的Sql执行流程。这个四个组件分别是,执行器(Execute)、Sql处理器(StatementHandler)、参数处理器(ParameterHandler)、结果集处理器(ResultSetHandler)。这个目标又可分解成4个小目标
理解四个组件执行上的顺序
每个组件的作用及意义
掌握Execute 的三个实现逻辑
掌握StatementHandler的三个实现逻辑
这个目标不是最难的,但是最要的。如果有一天你把学的MyBatis源码忘的差不多了,也希望你这个流程不要忘,这是MySql源码的主干和肪脉络。顺着它就可以把MyBatis知识在拾回来。
说明:关于参数处理、结果集处理其主要逻辑细节在于映射处理,可以放到映射篇章学习。

第二个要搞定的目标,就是SQL执行流程当中,所需要的三大映射:分别是动态SQL映射、参数映射、以及结果集映射。不仅要搞清楚映射配置的加载与解析,更重要的弄清楚映射逻辑。
加载解析:指用户配置的XML解析成JAVA配置对象。比如<Select>....</Select>块,最终要解析成SqlSource。或<resultMap>...</resultMap> 要解析成ResultMap对象。
映射逻辑:比如SqlSource如何基于参数生成可执行SQL,ResultMap如何将结果集,解析成JAVA对象等。
这里面涉及内容非常多,同样可以划分成一个个小目标:

前面两个目标如果搞定了,恭喜你,MyBatis实质上你已经拿下了,接下来的学习就会有一种水道渠成的轻松。

所谓扩展指对主流程的延伸。如:通过执行器可以完成所有功能,但使用太繁锁,会话(SqlSession)正是为简化它的使用。Mapper接口的设定也是一样,是为进一步简化使用会话。
除了针对易用性扩展,还有提高执行性能的扩展,比如:缓存、懒加载等。最后的插件机制则是暴露给用户自定义的扩展。接下来我们继续分解学习目标:

MyBatis源码最佳学习路径是,先抓主流程、在深入映射细节、最后在回到主流程的扩展上来,始终都要以主流为肪络。
在学习过程中,主流程是重点、映射细节是难点、其中结果集映射是难点中的难点。我们要保证先掌握重点,在去攻克难点。
学习之前一定要先划定目标,否则容易放弃。之所以学了就忘,是因为知识体系不够完整,必须要完整的学习MyBatis。
虽然MyBatis三个框架中源码量最少,但毕竟还是有上千个类、4万多行代码。面对这个体量我们一定要化繁为简,抓住核心。否则很容易陷入无边的逻辑细节当中。
最后祝大家,早日能拿下MyBatis去洞房,生出一个叫“知识”的娃儿来。
