非常棒的学习规划!这清晰地勾勒出了MyBatis源码学习的核心脉络和进阶路径。你的分解逻辑性很强,从主干到分支,从核心到扩展,非常适合系统性学习。
下面,我对你的三个目标进行一些补充和结构化,希望能让你的学习过程更顺畅。
第一目标:SQL执行流程(主干与脉络)
这是理解MyBatis如何工作的"生命线"。务必做到能画图、能口述。
-
宏观流程(简化版) :
SqlSession->Executor->StatementHandler->ParameterHandler->JDBC Statement->ResultSetHandler-> 返回结果。 -
四大组件深度解析:
- 执行器
Executor:- 作用 :调度中心,控制一级缓存、事务、延迟加载等,并最终将请求委派给
StatementHandler。 - 三个关键实现 :
SimpleExecutor:默认。每次执行都创建新的Statement,用完关闭。ReuseExecutor:重用预处理Statement对象。基于SQL字符串作为键进行缓存。BatchExecutor:用于批处理,将多个更新操作的Statement缓存起来,最后统一执行。
- 设计模式 :模板方法模式(
BaseExecutor定义了骨架,子类实现具体步骤)。
- 作用 :调度中心,控制一级缓存、事务、延迟加载等,并最终将请求委派给
- SQL处理器
StatementHandler:- 作用 :与JDBC
Statement交互的直接负责人,负责创建Statement、参数设置、执行SQL。 - 三个关键实现(与
Executor类似,但关注点不同) :SimpleStatementHandler:处理普通的Statement。PreparedStatementHandler:处理PreparedStatement(最常用,防SQL注入)。CallableStatementHandler:处理CallableStatement(存储过程)。
- 为什么分开 :
Executor关注执行策略(缓存、批处理),StatementHandler关注与JDBC API的具体交互方式。
- 作用 :与JDBC
- 参数处理器
ParameterHandler:- 作用 :将用户传入的Java参数,按照
ParameterMapping规则,设置到PreparedStatement中。 - 核心逻辑 :类型处理(
TypeHandler)的用武之地。
- 作用 :将用户传入的Java参数,按照
- 结果集处理器
ResultSetHandler:- 作用 :将
ResultSet结果集,根据ResultMap规则,映射成Java对象列表。 - 这是最复杂的一块,但第一阶段先理解其接口位置和作用即可。
- 作用 :将
- 执行器
学习建议:动手调试一个最简单的查询方法,跟着调试器一步步走完这个流程,画出时序图。
第二目标:映射配置(血肉与难点)
这是MyBatis强大灵活性的来源,也是最复杂的地方。
-
MappedStatement:- 定位 :它是核心配置的承载者 。一个
<select|insert|update|delete>标签或一个@Select注解的最终形态。 - 与三大映射的关系 :它内部包含了
SqlSource(动态SQL)、ParameterMap(历史遗留,现多用ParameterHandler中的TypeHandler)、ResultMap(结果映射)。可以把它看作一个执行命令的所有蓝图。
- 定位 :它是核心配置的承载者 。一个
-
动态SQL (
SqlSource):- 解析过程 :XML/注解中的SQL文本 ->
XMLScriptBuilder解析 -> 形成包含SqlNode(如IfNode,TrimNode)的抽象语法树 -> 动态拼接。 SqlSource主要实现 :DynamicSqlSource:包含动态标签(<if>,<where>等),执行时根据参数动态生成SQL。RawSqlSource:静态SQL,在启动时即可确定SQL字符串,性能稍好。ProviderSqlSource:用于注解中使用@InsertProvider等场景。
- 映射工具包
ognl:MyBatis早期使用OGNL表达式语言来解析#{},${}中的表达式,获取参数值。
- 解析过程 :XML/注解中的SQL文本 ->
-
参数映射:
- 过程 :Java方法参数 -> 转换为
ParamMap(处理@Param和多参数) -> 通过TypeHandler为每个ParameterMapping设置值。 - 关键点 :理解
ParameterMapping的组成(属性名、Java类型、JDBC类型、TypeHandler)。
- 过程 :Java方法参数 -> 转换为
-
结果集映射 (
ResultMap) - 最难部分:- 核心逻辑 :
ResultSetWrapper包装结果集元数据,ResultMap描述映射规则,通过ResultHandler进行组装。 - 关联映射(
<association>,<collection>) :- 嵌套查询 (Nested Select):触发子查询,可能引起N+1问题。
- 嵌套结果 (Nested Results):通过多表联查一次性查出,在内存中通过结果集处理器进行对象组装。
- 循环依赖:MyBatis通过"提前暴露对象"的方式解决。例如,A中有B属性,B中有A属性,在创建A的早期就将半成品A放入缓存,供B创建时引用,最后再完善A。
- 延迟加载:基于动态代理(Javassist或CGLIB),在访问关联属性时才触发查询。
- 自动映射 :当
ResultMap不完整或未配置时,按照列名-属性名的规则(开启autoMapping时)进行映射。
- 核心逻辑 :
-
配置中心
Configuration:- 定位 :MyBatis的单例工厂和配置仓库 。几乎所有组件(
MappedStatement,ResultMap,Executor等)都从这里创建和获取。 - 加载解析 :
XMLConfigBuilder解析mybatis-config.xml,XMLMapperBuilder解析Mapper.xml,最终将所有信息注册到Configuration的各个Map容器中。
- 定位 :MyBatis的单例工厂和配置仓库 。几乎所有组件(
学习建议 :分而治之。先攻克 SqlSource,再研究简单的 ResultMap(无关联),最后集中火力攻击关联映射、延迟加载和循环依赖。多写测试用例,观察不同配置下生成的SQL和执行过程。
第三目标:扩展支撑(皮肤与生态)
让MyBatis更好用、更强大的外围系统。
-
会话
SqlSession:- 设计模式 :门面模式 。对外提供简洁的API(如
selectOne),对内隐藏了Executor等复杂组件的交互细节。 - 意义 :是用户与MyBatis交互的主要入口。
- 设计模式 :门面模式 。对外提供简洁的API(如
-
Mapper接口动态代理:
- 核心 :
MapperRegistry负责管理接口与代理工厂MapperProxyFactory。 - 流程 :调用
Mapper接口方法 -> 被MapperProxy(InvocationHandler)拦截 -> 将方法名和参数转换为MappedStatement的ID -> 交给SqlSession执行。 MapperMethod:这是转换的关键类,它封装了SQL命令的类型(增删改查)和执行逻辑。
- 核心 :
-
缓存:
- 一级缓存 :在
BaseExecutor中,基于PerpetualCache实现,作用域为SqlSession(默认开启)。- 失效场景 :更新操作、手动清空、配置刷新、不同的
SqlSession。
- 失效场景 :更新操作、手动清空、配置刷新、不同的
- 二级缓存 :在
CachingExecutor(装饰器模式)中实现,作用域为Mapper级别 (需手动开启)。- 结构 :
TransactionalCacheManager管理事务性的缓存提交。它是一个责任链,最终的存储也是PerpetualCache,但可以被RedisCache等第三方缓存装饰。
- 结构 :
- 执行顺序:二级缓存 -> 一级缓存 -> 数据库。
- 一级缓存 :在
-
插件机制:
- 底层原理 :动态代理 + 责任链模式(
InterceptorChain)。 - 可拦截的四大组件 :
Executor,StatementHandler,ParameterHandler,ResultSetHandler。 - 关键对象 :
Interceptor(用户实现)、Plugin(包装类,利用InvocationHandler生成代理对象)、@Signature注解(指定拦截点)。
- 底层原理 :动态代理 + 责任链模式(
-
懒加载:
- 实现 :是结果集映射 的一个特性。当配置
fetchType="lazy"时,关联对象会被替换成一个由Javassist或CGLIB生成的代理对象。 - 触发 :当调用代理对象的 getter 方法时,代理对象会调用
ResultLoader去执行延迟的SQL查询。
- 实现 :是结果集映射 的一个特性。当配置
学习建议 :这部分相对有趣。可以尝试自己写一个简单的插件(如打印SQL执行时间),理解代理链的生成过程。深入研究 CachingExecutor 如何装饰普通的 Executor,理解装饰器模式的应用。
总结与学习路线图
你的规划已经非常完美。我再提供一个建议的学习顺序:
-
第一阶段(攻克主干):
- 从
SqlSession.selectOne()开始调试。 - 理清
Executor->StatementHandler->ParameterHandler->ResultSetHandler的调用链。 - 理解
Configuration和MappedStatement如何为这条链提供"蓝图"。
- 从
-
第二阶段(深入映射):
- 先搞懂静态SQL (
RawSqlSource) 如何工作。 - 再研究动态SQL (
DynamicSqlSource) 的解析和拼接。 - 重点攻坚:结果集映射。从简单映射到自动映射,再到关联映射,最后研究延迟加载和循环依赖。
- 先搞懂静态SQL (
-
第三阶段(掌握扩展):
- 理解
SqlSession和Mapper接口如何简化使用(门面+动态代理)。 - 研究缓存机制(一二级缓存的实现与区别)。
- 动手编写插件,理解拦截原理。
- 最后通读懒加载的实现,它融合了动态代理和结果集映射。
- 理解
记住,带着问题看源码 (比如"<if>标签是怎么实现的?"、"@Param注解是怎么生效的?"),并善用IDE的调试功能,你的学习效率会非常高。
你这份学习计划已经超越了绝大多数人,坚持下去,你对MyBatis的理解将达到架构师级别。祝你学习顺利!