MyBatis 架构设计进阶教学——顶层抽象接口设计(设计者视角·零基础易懂版)
前言
绝大多数人学习 MyBatis,都停留在「会用」的层面:会写 XML、会写 Mapper 接口、会做 CRUD 业务开发。但本套课程完全跳出使用层,站在框架设计者的角度,带你搞懂 MyBatis 最核心的底层根基——顶层接口抽象设计。
这是 MyBatis 整个架构的起点和根基,所有的插件机制、动态 SQL、执行流程、配置驱动,全部都是基于这一套顶层接口延伸出来的。
学完本章,你彻底告别「只会用框架」的普通开发者,拥有框架设计思维,能回答出所有高级面试核心问题:MyBatis 为什么要拆分这五大核心接口?为什么不直接使用原生 JDBC?MyBatis 的分层解耦思想到底是什么?
本章严格约束:全程不讲解 XML 配置、不讲解动态 SQL 使用、不讲解 Mapper 开发、不讲解业务实操,只讲架构分层、接口设计、底层思想。
一、本章核心学习目标(必看)
读完本章,你将彻底理解 5 个核心底层问题,做到零基础吃透 MyBatis 顶层架构:
-
原生 JDBC 存在哪些致命缺陷,迫使 MyBatis 必须重新设计框架?
-
MyBatis 为什么不直接复用 JDBC 原生 API,非要自定义一套全新的接口体系?
-
五大核心接口(Executor、StatementHandler、ParameterHandler、ResultSetHandler、SqlSession)的拆分逻辑是什么?是随意拆分还是遵循严格设计原则?
-
SqlSession 门面模式的设计意义,为什么禁止用户直接操作底层执行器?
-
五大接口的职责边界、层级关系、依赖关系,彻底搞懂 MyBatis 如何对 JDBC 做全方位抽象重构。
二、前置认知:原生 JDBC 完整运行模型
想要懂 MyBatis 的设计,必须先懂原生 JDBC 的原始流程。MyBatis 所有的接口拆分,都是为了解决 JDBC 的原生问题。我们先用最简单的文字流程图,还原一次完整的 JDBC 数据库查询操作。
2.1 原生 JDBC 完整执行链路(文字流程图)
获取数据库连接 Connection → 创建 SQL 预处理对象 PreparedStatement → 手动给 SQL 占位符绑定参数 → 执行 SQL 语句(查询/新增/修改/删除) → 获取结果集 ResultSet → 手动遍历结果集、封装为 Java 对象 → 关闭所有资源
2.2 原生 JDBC 代码核心特征
所有 JDBC 原生代码,都是一段臃肿的串行代码,所有逻辑全部揉在一起:
-
连接获取逻辑
-
SQL 语句定义逻辑
-
参数赋值逻辑
-
SQL 执行逻辑
-
结果集解析封装逻辑
-
资源关闭逻辑
没有任何分层、没有任何拆分,所有功能耦合在一块。
三、原生 JDBC 的 5 个致命缺陷(MyBatis 诞生的根本原因)
很多开发者只会说「JDBC 代码繁琐」,但框架设计者看到的是架构级缺陷,这才是 MyBatis 重构 JDBC、自定义接口体系的核心原因。
缺陷1:代码高度耦合,所有逻辑融为一体
JDBC 所有操作写在同一个代码块中,参数绑定、SQL 执行、结果解析、资源管理完全绑定。如果我只想修改「参数绑定规则」,必须改动整块代码;如果我想调整「结果封装逻辑」,也要侵入核心业务代码,完全不符合工程化设计。
缺陷2:代码重复率极高,冗余严重
所有数据库操作,都要重复写「获取连接、创建预处理对象、关闭资源」这一套模板代码。每个业务 Dao 都要重复编写,大量无效重复劳动,开发效率极低。
缺陷3:无扩展性,无法自定义增强逻辑
原生 JDBC 是固定的执行流程,没有任何扩展入口。如果我们需要实现分页、SQL 日志打印、多租户数据隔离、参数加密等通用功能,只能硬改每一处 JDBC 代码,没有统一的增强入口,完全无法统一管控。
缺陷4:不可插拔,组件无法替换
JDBC 的参数处理、结果处理、SQL 执行是固定绑定的,无法单独替换某一个环节的逻辑。比如我想自定义一套结果集封装规则,原生 JDBC 没有任何预留接口,无法实现组件替换。
缺陷5:维护成本极高,业务与底层逻辑混杂
业务代码中混杂着大量底层数据库操作逻辑,一旦数据库规则、参数绑定规则、结果解析规则变更,所有业务代码都需要修改,维护风险极大。
四、MyBatis 核心架构:JDBC 重构与接口拆分逻辑
弄懂了 JDBC 的缺陷,我们就能理解 MyBatis 的核心设计思想:不修改 JDBC 的底层功能,只对 JDBC 臃肿耦合的流程做「分层拆分、职责解耦、接口抽象」。
MyBatis 没有创造新的数据库执行逻辑,只是把 JDBC 揉成一团的代码,按照单一职责原则,拆分成 4 个核心功能接口,再新增一个门面接口统一对外暴露。
4.1 JDBC 与 MyBatis 接口一一对应拆分关系
这是本章最核心的拆分逻辑,所有 MyBatis 底层执行,全部对应 JDBC 原生步骤,一一映射、没有多余设计:
-
JDBC 整体执行调度、流程管控 → Executor(执行器)
-
JDBC Statement 语句创建、SQL 操作管理 → StatementHandler(语句处理器)
-
JDBC 占位符参数赋值、参数绑定 → ParameterHandler(参数处理器)
-
JDBC ResultSet 结果集解析、对象封装 → ResultSetHandler(结果处理器)
最后新增一个门面接口:SqlSession,统一对外提供数据库操作入口,隐藏上述四大底层接口的所有复杂逻辑。
4.2 MyBatis 顶层接口分层结构图(文字完整版)
用户调用层:SqlSession(统一门面入口,面向开发者)
↓ 底层调度依赖
核心执行层:Executor(总调度,管控整个 SQL 执行全流程)
↓ 细分职责拆分
功能组件层:
1. StatementHandler:负责 SQL 语句的创建与执行
2. ParameterHandler:负责 SQL 参数的绑定赋值
3. ResultSetHandler:负责查询结果的封装返回
↓ 最终落地
原生底层:JDBC 原生 API(Connection、PreparedStatement、ResultSet)
核心总结:MyBatis 五层架构,层层封装、层层解耦,每一层只做自己的事,完全遵循单一职责原则。
4.3 五大核心接口完整职责对照表
|
核心接口名称 |
核心架构职责 |
对应 JDBC 原生环节 |
架构层级 |
|---|---|---|---|
|
SqlSession |
统一对外门面入口,封装所有底层执行逻辑,提供增删改查通用方法,管控会话生命周期 |
无直接对应,是 MyBatis 自定义门面层 |
用户接入层(最高层) |
|
Executor |
SQL 执行总调度,管控全流程,管理缓存、事务,调度三大 Handler 执行工作 |
Connection 获取、SQL 整体执行调度 |
核心调度层 |
|
StatementHandler |
创建 JDBC 预处理语句,执行 SQL 命令,管控语句生命周期 |
PreparedStatement 创建、SQL 执行 |
核心功能层 |
|
ParameterHandler |
解析方法参数,给 SQL 占位符批量赋值,处理参数类型转换 |
PreparedStatement 参数 set 赋值 |
核心功能层 |
|
ResultSetHandler |
接收 SQL 执行结果集,通过反射自动封装为 Java 实体对象 |
ResultSet 遍历、数据封装 |
核心功能层 |
五、MyBatis 核心灵魂:完整调用链架构全解析(设计者视角)
上一章我们搞定了 MyBatis 顶层五大接口的拆分设计,解决了「MyBatis 为什么要拆接口」的架构问题。本章我们落地MyBatis 唯一核心执行链路,这是 MyBatis 的灵魂所在,也是框架设计者最精妙的分层设计。
本章不讲用法、不讲配置,只解决一个核心问题:我们手写的 Mapper 接口方法,没有实现类,到底是如何一步步调用到数据库、最终返回 Java 对象的?
读完本章,你可以闭着眼手写完整调用链路,彻底吊打 90% 只会用 MyBatis 的开发者,从根源理解 MyBatis 的分层解耦、扩展设计。
5.1 前置核心认知
所有 MyBatis 数据库操作,永远只会走这一条固定链路,没有任何例外。这是框架固化的顶层执行流程,所有功能、插件、扩展全部基于这条链路实现。
5.2 标准完整调用链流程图(必背·文本完整版)
Mapper接口方法调用 → MapperProxy(动态代理拦截) → SqlSession(门面统一调度) → Executor(全局执行调度) → StatementHandler(创建执行SQL语句) → ParameterHandler(参数绑定赋值) → 底层JDBC原生API执行 → ResultSetHandler(结果集封装反射) → 返回Java实体对象
5.3 逐环节深度拆解(核心类 + 执行职责 + 设计目的)
我们以最常用的 userMapper.selectById() 查询方法为例,逐步骤拆解每一层的工作逻辑、对应核心源码类,以及设计者这么分层的核心目的。
第一步:Mapper 接口方法调用(用户层)
核心特征:只有接口,无任何实现类,无业务代码,仅定义方法签名。
对应核心类:自定义 Mapper 接口(无源码实现类)
执行职责:开发者直接调用接口方法,发起数据库查询请求。
设计目的:面向接口编程,彻底解耦业务层与数据库底层,让开发者只关注业务调用,无需关心任何JDBC底层逻辑。
第二步:MapperProxy 动态代理拦截(核心中转层)
对应核心源码类:org.apache.ibatis.binding.MapperProxy
执行职责:JDK动态代理核心实现,拦截 Mapper 接口的所有方法调用。
核心工作逻辑:
-
开发者调用接口方法时,因为接口无实现类,所有请求会被 MapperProxy 代理对象拦截;
-
提取当前调用的接口全限定名 + 方法名,精准匹配全局缓存中的 MappedStatement(SQL元数据);
-
将拦截到的方法请求,统一转发给 SqlSession 处理。
设计目的:这就是「为什么不用写DAO实现类」的核心答案!通过动态代理自动生成实现逻辑,省去重复的DAO实现类开发,同时统一拦截所有Mapper请求,实现全局统一管控。
第三步:SqlSession 门面调度(统一入口层)
对应核心源码类:org.apache.ibatis.session.defaults.DefaultSqlSession
执行职责:MyBatis 统一门面入口,承接所有代理转发的数据库请求。
核心工作逻辑:
-
接收 MapperProxy 转发的方法请求;
-
根据方法信息匹配对应的 SQL 元数据;
-
将请求下发给底层真正的执行器 Executor。
设计目的:门面模式核心体现!屏蔽底层所有复杂组件(Executor、三大Handler),对外提供统一、简洁的调用入口,避免用户直接操作底层核心组件,降低使用门槛、规避底层操作风险。
第四步:Executor 全局执行调度(核心总指挥)
对应核心源码类:org.apache.ibatis.executor.SimpleExecutor(默认简单执行器)
执行职责:整条链路的总调度、总管控,是MyBatis执行层的核心枢纽。
核心工作逻辑:
-
接收 SqlSession 下发的执行请求;
-
管控数据库连接、事务、一级/二级缓存;
-
根据执行需求,调度 StatementHandler、ParameterHandler、ResultSetHandler 三大组件依次工作;
-
兜底管控整条SQL执行全流程。
设计目的:统一调度底层所有执行组件,实现执行流程统一管控、缓存统一管理、事务统一控制,让三大Handler只专注自身单一职责。
第五步:StatementHandler SQL语句处理(语句管理层)
对应核心源码类:org.apache.ibatis.executor.statement.StatementHandler
执行职责:专门负责JDBC语句对象的创建与SQL执行。
核心工作逻辑:
-
接收Executor调度指令;
-
创建JDBC原生
PreparedStatement预处理对象; -
定义最终要执行的SQL语句模板;
-
等待参数绑定完成后,执行SQL语句。
设计目的:单独拆分SQL语句创建、执行、生命周期管理逻辑,和参数处理、结果处理彻底解耦,支持单独扩展SQL执行逻辑。
第六步:ParameterHandler 参数绑定(参数处理层)
对应核心源码类:org.apache.ibatis.executor.parameter.ParameterHandler
执行职责:专门负责SQL占位符的参数赋值、类型转换。
核心工作逻辑:
-
解析方法传入的参数数据;
-
自动完成Java类型→数据库类型的转换;
-
给PreparedStatement的SQL占位符逐一赋值,补全完整可执行SQL。
设计目的:将繁琐、易变的参数处理逻辑单独抽离,统一参数绑定规则,支持自定义参数加密、脱敏、类型转换等扩展功能。
第七步:JDBC 原生执行(底层落地层)
对应核心源码:JDBC原生API
执行职责:执行完整SQL语句,访问数据库,获取原始结果集 ResultSet。
核心工作逻辑:调用原生JDBC方法,向数据库发起SQL请求,数据库执行后返回原始数据结果集。
设计目的:MyBatis 不重构数据库底层通信逻辑,仅做上层封装,保证底层稳定性,同时完全屏蔽原生JDBC的繁琐操作。
第八步:ResultSetHandler 结果封装(数据返回层)
对应核心源码类:org.apache.ibatis.executor.resultset.ResultSetHandler
执行职责:专门负责数据库原始结果集→Java实体对象的自动映射。
核心工作逻辑:
-
接收JDBC返回的原始ResultSet结果集;
-
通过Java反射机制,读取结果集字段;
-
自动封装、映射为开发者定义的POJO实体对象;
-
关闭资源,返回封装后的Java对象。
设计目的:彻底解放手动封装结果集的重复工作,统一ORM映射规则,支持自定义结果映射、字段脱敏等扩展。
5.4 五大核心灵魂问题深度解答(设计者视角·必懂)
本节解答所有开发者最核心的疑惑,彻底打通调用链设计思想,知其然更知其所以然。
问题1:Mapper接口为什么不需要、也没有实现类?
普通Java接口必须有实现类才能调用,而MyBatis的Mapper接口不需要,核心原因是:实现类被 MapperProxy 动态代理「动态生成」了。
问题2:MapperProxy 是如何工作的?
MapperProxy 的核心工作只有三个字:拦截转发。
它不会执行 SQL、不会处理参数、不会封装结果。它只做一件事:开发者调用 Mapper 接口方法 → 拦截 → 拿到「类名+方法名」→ 找到对应的 SQL 元数据 → 丢给 SqlSession 执行。
这是典型的代理模式解耦:把「用户调用」和「底层执行」彻底分开。
问题3:为什么要经过 SqlSession,不直接调用 Executor?
核心设计思想:门面模式(Facade)。
Executor、StatementHandler、ParameterHandler 属于底层执行组件,层级极低、细节极多、极易用错。如果直接暴露给业务层,开发者需要感知连接、事务、执行器细节,代码会重回 JDBC 时代的混乱。
SqlSession 的唯一价值:屏蔽底层所有复杂细节,对外提供统一、干净、安全的调用入口。
同时它管控会话生命周期:统一提交、回滚、关闭资源,保证整个数据库操作的规范性。
问题4:Executor 在整个流程中的地位是什么?
一句话定位:整条执行链路的总指挥、大脑。
SqlSession 只是对外门面,真正干活、调度、管控的核心是 Executor。
它负责:接管执行请求、管理缓存、控制事务、调度三大 Handler 有序执行,是 MyBatis 执行层的核心枢纽。
问题5:为什么要拆成三个 Handler?
为了单一职责 + 可插拔扩展,这是框架可拓展性的根基。
原生 JDBC 将「语句创建、参数赋值、结果封装」揉成一团,无法单独修改某一个环节的逻辑。拆分后:
-
想改参数规则 → 只改 ParameterHandler
-
想改 SQL 执行规则 → 只改 StatementHandler
-
想改结果映射规则 → 只改 ResultSetHandler
后续 MyBatis 所有插件(分页、日志、多租户、数据脱敏),全部依托这三个独立扩展点实现。
5.5 本章顶层设计思想总结
MyBatis 完整调用链,本质是一套分层解耦、逐层委派、职责单一、可无限扩展的工程化执行架构:
-
代理层解耦:MapperProxy 消除 DAO 实现类,简化开发;
-
门面层屏蔽:SqlSession 屏蔽底层复杂度,统一入口;
-
执行层调度:Executor 统一管控全流程,保证执行秩序;
-
组件层拆分:三大 Handler 各司其职,预留扩展点;
-
底层复用:基于原生 JDBC 执行,保证稳定可靠。
六、MyBatis 核心架构:配置驱动体系(设计者视角·核心进阶)
前面两章我们搞定了「顶层接口抽象」和「运行时调用链路」,解决了MyBatis 怎么执行的问题。本章我们解决一个更底层的问题:MyBatis 的 SQL 从哪里来?为什么不用运行时字符串拼接?什么是配置驱动?
本章是 MyBatis 脱离 JDBC 硬编码、成为现代化框架的核心分水岭。读完本章,你将彻底理解:MyBatis 不是在运行时拼 SQL,而是启动时预解析 SQL、元数据固化、运行时直接执行的高级设计思想。
6.1 本章核心学习目标
零基础吃透 MyBatis 配置驱动核心架构,彻底理解四大核心问题:
-
Configuration 全局容器的架构定位与内部存储结构;
-
XML 配置文件如何被一步步解析、加载、转化为程序可识别数据;
-
MappedStatement 的核心作用,为什么它是 SQL 的元数据载体;
-
为什么说 XML 不是配置文件,而是「SQL 元数据定义语言」。
最终能力:清晰说出 MyBatis「启动预解析、运行直接执行」的核心优势,区别于原生 JDBC 运行时拼接字符串的弊端。
6.2 前置认知:JDBC 硬编码的终极痛点
原生 JDBC 除了代码耦合、冗余之外,还有一个致命工程化缺陷:SQL 是运行时动态字符串,无统一管理、无预校验、无结构化存储。
原生 JDBC 的问题:
-
SQL 散落嵌套在业务代码中,无法统一维护;
-
所有 SQL 都是运行时拼接,容易出现语法错误、注入漏洞;
-
没有统一的元数据记录,无法统一拦截、修改、监控 SQL;
-
参数、返回值、SQL 语句没有结构化定义,完全混乱。
MyBatis 配置驱动体系,就是为了彻底根治这个问题而生的。
6.3 核心流程总览(必背流程图)
这是 MyBatis 配置驱动的唯一核心链路,所有配置、所有 SQL 全部走这套流程:
XML资源文件 → XMLConfigBuilder/XMLMapperBuilder 解析器 → 解析结构化数据 → 存入 Configuration 全局容器 → 封装为 MappedStatement 元数据 → 运行时直接读取执行
核心区别:JDBC 是「运行时拼接SQL」,MyBatis 是「启动时解析SQL、运行时直接取用」
6.4 四大核心类深度拆解(架构定位+职责+设计动机)
MyBatis 配置驱动体系,完全由这四个核心类支撑,缺一不可,我们从设计者视角逐一拆解。
1)Configuration:全局唯一配置元数据容器
架构定位:MyBatis 整个框架的全局大管家、内存仓库,项目启动后永久常驻内存。
核心本质:一个超大的结构化存储容器,所有配置、所有SQL、所有映射规则、所有环境参数,全部统一存在这里。
内部核心存储结构(全是Map,键值存储,快速查询):
-
Map<String, MappedStatement> mappedStatements:存储所有SQL语句元数据(核心中的核心);
-
类型别名Map、插件Map、环境配置Map、缓存配置Map、映射关系Map等。
设计目的:
-
中心化管理:把散落的XML配置、SQL语句全部收拢到一个全局容器,统一管控;
-
预加载预解析:项目启动时一次性解析完毕,运行时不再解析XML,提升执行性能;
-
全局可访问:后续 Executor、Handler、插件所有组件,需要配置和SQL时,直接从Configuration获取。
2)XMLConfigBuilder:全局主配置解析器
架构定位:负责解析mybatis-config.xml全局主配置文件的专属解析器。
核心职责:
-
读取全局环境配置(数据库连接、事务、运行参数);
-
解析全局插件、别名、设置项;
-
记录 Mapper 映射文件路径,交给 Mapper 解析器处理。
设计目的:拆分解析职责,专门负责全局框架配置,和业务SQL解析彻底解耦。
3)XMLMapperBuilder:Mapper映射文件解析器
架构定位:专门解析所有 Mapper.xml 文件的专属解析器(业务SQL解析核心)。
核心职责:
-
读取每一个 Mapper.xml 文件内容;
-
解析每一条 select/insert/update/delete 标签;
-
提取 SQL 语句、参数类型、返回值类型、动态标签规则;
-
将每一条 SQL 封装成独立的 MappedStatement 对象;
-
存入 Configuration 的 mappedStatements Map 中。
设计目的:将「业务SQL配置」和「框架全局配置」拆分解析,职责单一、结构清晰、方便扩展。
4)MappedStatement:SQL元数据最小单元
架构定位:MyBatis 中单条SQL的完整元数据载体,是整个配置驱动体系的最终落地产物。
核心本质:把XML中一条SQL的所有信息,结构化、对象化、固化到内存中。
一个 MappedStatement 内部包含所有信息:
-
当前 SQL 的唯一 ID(接口全类名+方法名);
-
原始 SQL 模板语句;
-
参数类型、参数映射规则;
-
返回值类型、ResultMap映射规则;
-
动态SQL标签规则、缓存规则、执行类型。
设计目的:让每一条SQL不再是零散字符串,而是拥有完整属性、可被程序识别、可被拦截修改的结构化对象。
6.5 完整配置解析分步流程(启动阶段全过程)
MyBatis 在项目启动、初始化 SqlSessionFactory 时,会一次性完成所有解析,全程只执行一次:
-
第一步:读取配置文件:加载 mybatis-config.xml 全局配置和所有 Mapper.xml 映射文件;
-
第二步:全局配置解析:XMLConfigBuilder 解析全局环境、参数、插件配置,初始化 Configuration 基础容器;
-
第三步:批量解析Mapper文件:XMLMapperBuilder 遍历所有Mapper文件,逐标签解析SQL;
-
第四步:封装元数据:每解析一条SQL,自动组装成一个 MappedStatement 对象;
-
第五步:全局缓存存储:以「方法全限定名」为Key,MappedStatement为Value,存入Configuration的Map集合;
-
第六步:等待调用:程序运行期间,不再解析XML,需要执行SQL时,直接从Configuration中取出对应MappedStatement执行。
6.6 核心灵魂问题:为什么 XML 本质是「SQL元数据定义语言」?
普通开发者认为:XML 是用来写 SQL 的配置文件。
框架设计者视角:XML 是用来「定义SQL元数据的结构化语言」。
两者的核心区别:
-
普通用法:XML 只是存放SQL字符串;
-
框架设计:XML 定义了SQL语句、参数规则、返回映射、动态规则、缓存规则的全套元数据。
XML 的每一个标签、每一个属性,最终都会被解析成 MappedStatement 的属性,固化到内存。它不是简单的文本配置,是一套专门用来描述数据库操作的领域语言。
6.7 顶层设计思想升华(三大核心思想)
思想1:配置驱动
所有执行行为、SQL规则、映射规则,全部由外部配置定义,而非代码硬编码。修改SQL、修改映射规则无需改Java代码,无需重启核心逻辑,解耦业务与底层。
思想2:元数据化
将零散的SQL字符串,转化为结构化、对象化、可管理、可扩展的元数据对象(MappedStatement),让框架拥有操控SQL的能力。
思想3:运行前解析(核心性能&架构优势)
JDBC:运行时拼接SQL(每次请求都要拼接、易错、低效、无法预校验);
MyBatis:启动时解析SQL(启动一次性解析完毕,运行时直接读取执行,高效、稳定、可统一拦截)。
6.8 本章终极验收
读完本章,你可以清晰回答:
MyBatis 为什么比原生 JDBC 更先进?
因为 MyBatis 把「散落的SQL硬编码」变成了「全局统一管理的结构化元数据」,把「运行时动态拼接」变成了「启动时预解析、运行时直接执行」,实现了可维护、可扩展、可管控、高性能的工程化数据库操作架构。
七、MyBatis 内核深度:动态SQL引擎设计(SQL模板引擎设计者视角)
前面章节我们掌握了 MyBatis 的接口分层、执行调用链、配置驱动体系。本章我们深入 MyBatis 最核心的内核组件——动态SQL引擎。
这是彻底区分「CRUD开发者」和「框架设计者」的关键章节:绝大多数人以为 MyBatis 是 ORM 框架,但从内核设计上,MyBatis 本质是一套轻量级、标签驱动的 SQL 模板引擎。
本章不讲动态SQL怎么用,只讲:MyBatis 是如何从零设计、实现出 <if>、<foreach> 等所有动态标签的,彻底拆解模板引擎的底层设计逻辑。
7.1 本章核心学习目标
读完本章,彻底吃透四大核心底层问题:
-
MyBatis 到底有没有使用专业 SQL 语法树(AST)?官方设计取舍是什么?
-
SqlNode 树形结构的本质是什么,如何承载所有动态标签逻辑?
-
DynamicSqlSource 核心工作原理,如何实现动态SQL延迟拼接?
-
动态SQL的真正拼接时机,为什么是运行时拼接而非启动时?
最终认知升级:彻底理解 MyBatis = 轻量级SQL模板引擎 + JDBC封装,并非传统全功能ORM框架。
7.2 前置痛点:原生动态SQL的致命问题
在没有模板引擎之前,Java实现动态查询只能硬编码字符串拼接:
-
判断参数非空,手动拼接
where、and、or; -
循环遍历参数,手动拼接
in条件; -
代码极度臃肿、可读性极差、极易出现SQL语法错误;
-
所有拼接逻辑写死在Java代码中,无法统一管理、无法扩展。
这种硬编码拼接方式,完全违背工程化设计思想,MyBatis 动态SQL引擎通过模板标签+树形解析彻底根治该问题,实现SQL逻辑与业务代码解耦。
7.3 核心误区硬核辟谣:MyBatis 不使用标准 SQL-AST
全网90%的讲解都是错误的:MyBatis 没有使用 SQL 抽象语法树(SQL AST)。
像 JPA、数据库内核、SQL 优化工具,会基于标准 SQL-AST 做词法分析、语法分析、语义校验、语句重构,体量极大、复杂度极高。
MyBatis 的设计权衡(核心架构智慧):
MyBatis 的定位是轻量级SQL执行框架,职责只限于「SQL模板渲染 + 参数绑定 + JDBC执行」,不需要做SQL语法校验、语义分析、语句优化。
因此 MyBatis 自研了一套轻量自定义树形结构:SqlNode,只解析 MyBatis 自定义标签(if/foreach/where/trim),不解析标准SQL语法,够用、极简、高性能。
7.4 动态SQL核心基石:SqlNode 树形架构
SqlNode 是 MyBatis 动态SQL的最小执行单元,顶层统一抽象接口。所有动态标签、静态SQL文本,全部是 SqlNode 的实现类,统一具备「运行时渲染SQL」的能力。
核心设计模式:组合模式,支持无限层级嵌套,完美适配复杂动态SQL组合场景。
7.4.1 核心 SqlNode 实现类职责拆解
-
SqlNode(顶层接口):定义统一渲染方法
apply(),所有节点统一执行SQL拼接逻辑,是树形结构的统一规范。 -
MixedSqlNode(根容器节点):核心容器,用于收纳一堆子SqlNode节点(静态文本、if标签、foreach标签等),所有动态SQL的根节点永远是 MixedSqlNode,负责统筹所有子节点。
-
IfSqlNode(条件节点):对应 <if> 标签,内置 test 表达式解析逻辑,运行时根据入参判断是否渲染当前节点的SQL片段。
-
ForeachSqlNode、WhereSqlNode、TrimSqlNode:各类动态标签专属节点,各司其职,独立实现对应渲染逻辑。
7.4.2 XML标签 → SqlNode 树形转换流程图(启动阶段)
原始XML模板:
<select id="selectUser"> SELECT * FROM user <if test="status != null"> AND status = #{status} </if> <foreach collection="ids" item="id"> OR id = #{id} </foreach> </select>
启动时解析生成SqlNode树:
【根节点:MixedSqlNode】
├─ 静态文本节点:SELECT * FROM user
├─ 条件节点 IfSqlNode:status非空判断分支
└─ 循环节点 ForeachSqlNode:ids集合循环分支
核心本质:项目启动时,XMLMapperBuilder 会把所有动态SQL静态模板,预编译为一棵可遍历、可执行、可判断的SqlNode树,全程无SQL拼接,仅完成结构固化。
7.5 动态SQL核心调度:DynamicSqlSource 延迟渲染机制
SqlNode树是静态结构,真正实现动态渲染、延迟拼接的核心类是 DynamicSqlSource。
7.5.1 核心工作原理
MyBatis 区分两种 SQL 数据源:
-
StaticSqlSource:无任何动态标签,启动时直接拼接完成SQL,运行时直接执行;
-
DynamicSqlSource:包含if/foreach等动态标签,启动时只建树、不拼接,运行时根据参数动态渲染。
7.5.2 为什么动态SQL必须运行时拼接?
核心答案:动态分支、循环次数完全依赖用户传入的运行时参数,启动时无任何入参,无法预判SQL最终形态。
这是「延迟构建」的核心设计思想:固定模板提前预编译,动态数据运行时填充,兼顾启动性能与动态灵活性。
7.6 最终产物:BoundSql(可执行SQL载体)
DynamicSqlSource 遍历 SqlNode 树、执行所有条件判断与循环渲染后,最终生成BoundSql 对象,这是交给JDBC执行的唯一成品。
BoundSql 内部封装三大核心信息:
-
完整渲染后的可执行SQL(带 ? 占位符);
-
完整参数映射列表与参数元数据;
-
动态渲染后的最终语句结构。
7.7 动态SQL完整闭环流程
阶段一:启动初始化阶段(预编译、建树、无拼接)
XMLMapperBuilder解析XML → 识别动态标签 → 生成多级SqlNode树 → 封装为DynamicSqlSource → 存入MappedStatement内存固化。
阶段二:运行调用阶段(遍历、渲染、拼接)
用户调用Mapper方法传入参数 → MappedStatement获取DynamicSqlSource → 遍历SqlNode树、执行test判断、循环渲染 → 拼接最终SQL → 生成BoundSql → 交由执行链路执行JDBC操作。
7.8 本章终极架构结论
MyBatis 绝对不是传统ORM框架,本质是:轻量级标签驱动SQL模板引擎 + JDBC通用封装。
Hibernate等ORM框架屏蔽SQL、自动生成SQL;而MyBatis核心价值是接管SQL的模板化、动态化、工程化管理,让手写SQL更优雅、可维护、可扩展。
八、MyBatis 高阶内核:插件扩展机制(Interceptor 可插拔架构专家视角)
前面章节我们吃透了 MyBatis 执行链路、配置驱动、动态SQL内核,本章我们学习 MyBatis 最核心的扩展架构——插件机制。
这是 MyBatis 具备高度可扩展性的根源,也是分页插件、多租户插件、SQL日志插件、数据脱敏插件的底层支撑。本章彻底解答:MyBatis 如何做到不修改源码、无侵入改写SQL执行流程。
8.1 本章核心学习目标
零基础吃透 MyBatis 插件架构,彻底掌握四大核心问题:
-
Interceptor 拦截器的核心定位与工作原理;
-
MyBatis 仅允许拦截4个核心接口的底层设计原因;
-
Plugin 类如何通过JDK动态代理实现对象包装与拦截;
-
多插件场景下,责任链+动态代理的协同执行逻辑。
最终能力:彻底理解插件如何拦截、修改、改写、增强原生SQL执行流程,具备自定义MyBatis插件的架构能力。
8.2 前置核心思想:MyBatis 自研 AOP 架构
首先纠正关键误区:MyBatis 插件机制不是 Spring AOP,是框架自研的轻量化AOP实现。
Spring AOP 基于动态代理、切点表达式,粒度细、功能重;而 MyBatis 为了保证轻量、高性能,自研了一套限定扩展点、可插拔、无侵入的AOP拦截架构。
核心设计理念:在不修改源码、不侵入核心流程的前提下,对核心执行组件做方法前置/后置增强,实现功能扩展。
8.3 核心定义:Interceptor 拦截器是什么?
Interceptor 是 MyBatis 插件的顶层核心接口,所有自定义插件必须实现该接口。
它是框架预留的统一扩展入口,本质是:核心组件方法的拦截增强器。
8.3.1 Interceptor 核心方法职责
-
intercept(Invocation invocation):核心拦截方法,所有增强逻辑(分页、日志、脱敏)全部写在这里,可拦截原方法、修改参数、修改SQL、修改返回结果。
-
plugin(Object target):包装目标对象,为原生核心组件生成代理对象,开启拦截能力。
-
setProperties():读取插件配置参数,实现可配置化。
8.4 唯一允许拦截的4个核心对象(扩展点可控设计)
MyBatis 严格限制插件拦截范围,全局只允许拦截四大顶层接口,无任何其他扩展点,这是框架刻意的架构约束。
四大可拦截目标(完全对应前文五大核心接口):
-
Executor:全局执行器(拦截SQL执行、事务、缓存全过程);
-
StatementHandler:SQL语句处理器(拦截SQL创建、执行,最常用扩展点,分页插件核心依赖);
-
ParameterHandler:参数处理器(拦截参数绑定、参数加密、脱敏);
-
ResultSetHandler:结果集处理器(拦截结果封装、数据脱敏、字段转换)。
8.4.1 灵魂问题:为什么只允许拦截这4个接口?
这是框架设计者的极致架构权衡,核心4点原因:
-
分层边界清晰:这4个接口是MyBatis执行层的唯一核心组件,职责单一、层级稳定,是所有SQL执行的必经链路;
-
可控扩展、防止滥用:如果开放所有类拦截,开发者可随意篡改框架任意逻辑,会导致架构混乱、版本不兼容、线上隐患;
-
保证内核稳定:框架核心内核(配置解析、动态SQL引擎)不允许被拦截,保证底层基础能力绝对稳定;
-
覆盖所有扩展场景:四大扩展点已经覆盖「执行、SQL、参数、结果」全流程,足以实现99%的业务扩展需求(分页、日志、多租户、脱敏、加密)。
核心总结:MyBatis 插件是有限制的可扩展,而非无限制随意篡改,兼顾扩展性与稳定性。
8.5 核心机制:Plugin 动态代理包装原理
插件能够实现拦截的核心底层支撑:Plugin.wrap() 动态代理包装机制。
所有四大核心组件(Executor/Handler)创建后,不会直接原生使用,而是经过 Plugin 包装,生成代理对象。
8.5.1 Plugin 核心工作流程
-
目标对象初始化:MyBatis 创建原生 Executor、StatementHandler 等核心对象;
-
Plugin.wrap 包装:检测当前是否存在自定义插件,若存在,通过JDK动态代理生成目标对象的代理类;
-
代理拦截方法:后续所有原生对象的方法调用,都会被Plugin代理拦截;
-
执行插件逻辑:优先执行自定义Interceptor的intercept增强逻辑,再执行原生方法,实现无侵入增强。
8.5.2 关键设计:JDK 动态代理适配
MyBatis 四大拦截目标全部是接口,完美适配JDK动态代理(仅支持接口代理),无需依赖CGLIB,保证框架轻量、无第三方依赖、高性能。
8.6 插件完整拦截流程图(必背文本版)
原生核心对象初始化 → Plugin.wrap() 生成代理对象 → 业务调用触发方法执行 → 进入Interceptor拦截方法(自定义增强逻辑) → 执行原生目标方法 → 后置增强处理 → 返回结果
8.7 多插件协同:责任链 + 动态代理嵌套机制
当项目中存在多个插件(如分页插件+日志插件)时,MyBatis 采用多层代理嵌套的责任链模式执行。
执行顺序规则:
-
插件配置顺序靠前的,先包装、后执行、最后结束;
-
多层代理层层嵌套,形成链式拦截,互不干扰、独立扩展;
-
所有插件遵循「前置增强→原生执行→后置增强」的统一秩序。
核心优势:插件完全可插拔,新增/删除插件无需修改任何业务代码、无需改动核心源码。
8.8 经典实战案例(架构层面解析,不讲用法)
案例1:分页插件(最经典插件设计)
拦截目标:StatementHandler
架构原理:
-
拦截 StatementHandler 的 SQL 预处理方法;
-
在原生SQL执行前,拦截原始查询语句;
-
根据分页参数,动态改写SQL,拼接 limit 分页语句;
-
执行改写后的分页SQL,实现无侵入分页。
核心价值:不修改Mapper XML、不改动业务代码,通过插件直接改变SQL执行内容。
案例2:SQL日志打印插件
拦截目标:Executor
架构原理:
-
拦截Executor全局执行方法,在SQL执行前后做增强;
-
前置打印:完整可执行SQL、参数信息、执行耗时;
-
后置打印:执行结果、影响行数;
-
不修改任何原生执行逻辑,仅做监控增强。
8.9 本章核心顶层设计思想升华
思想1:自研轻量化AOP思想
不依赖Spring AOP,框架内置AOP能力,专注数据库执行链路增强,轻量、高效、无冗余。
思想2:可插拔架构
所有扩展功能全部基于插件实现,插件可随时新增、移除、替换,核心业务与扩展功能完全解耦,符合开闭原则。
思想3:可控扩展点设计
框架主动收敛扩展点,只开放四大核心接口,既保证足够的扩展能力,又杜绝无限制篡改内核逻辑,实现扩展性与稳定性的完美平衡。
8.10 终极架构结论
MyBatis 插件可以改变SQL执行流程的核心原因:
插件通过 Plugin动态代理 + Interceptor拦截机制,在SQL执行的核心节点(SQL创建、参数绑定、语句执行、结果封装)进行前置拦截,可修改SQL语句、修改参数、修改执行逻辑、修改返回结果,全程无侵入、无源码修改,这就是MyBatis可扩展架构的终极内核。
8.11 本章验收标准
读完本章,你可以独立回答:
-
MyBatis 插件与Spring AOP的区别?
-
为什么框架只允许拦截四大核心接口?
-
Plugin.wrap 动态代理的核心工作流程?
-
分页插件能够改写SQL的底层原理是什么?
MyBatis 动态SQL引擎的设计初衷:用标签模板替代硬编码拼接,用树形结构统一管理所有动态分支。
7.3 核心误区纠正:MyBatis 不使用 SQL AST 语法树
这是 90% 开发者都会搞错的知识点,设计者视角精准辟谣:
MyBatis 没有使用专业的 SQL AST(抽象语法树)解析器。
专业数据库框架(如编译器、数据库内核、JPA)会做完整SQL词法分析、语法分析,生成标准AST语法树,校验SQL语法、优化SQL结构。
MyBatis 的替代方案:自定义标签驱动 SqlNode 树
它不解析SQL语法,只解析MyBatis自定义标签结构,是一套「轻量、专用、只为动态拼接服务」的自定义树形结构,而非标准SQL语法树。
设计权衡(核心架构取舍)
为什么 MyBatis 放弃标准 AST,自研 SqlNode 树?
-
规避复杂度爆炸:标准SQL AST需要兼容海量SQL语法、版本、方言,开发维护成本极高;
-
按需设计、够用即可:MyBatis只需要实现「条件分支、循环、文本拼接」基础模板能力,无需语法校验、SQL优化;
-
追求轻量高效:自定义SqlNode树体量极小、解析速度快、无冗余逻辑;
-
聚焦职责单一:MyBatis只负责SQL拼接与参数绑定,SQL优化、语法校验交给数据库本身。
一句话总结:MyBatis 动态SQL是「模板标签树」,不是「SQL语法树」。
7.4 核心核心:SqlNode 树形体系(动态SQL的基石)
SqlNode 是 MyBatis 动态SQL引擎的顶层抽象接口,所有动态标签、静态文本,全部是 SqlNode 的实现类。
核心设计思想:将XML中零散的SQL文本、动态标签,统一抽象为「树节点」,启动时组装成一棵完整的树,运行时遍历树、拼接SQL。
7.4.1 三大核心 SqlNode 实现类职责拆解
1)SqlNode(顶层接口)
核心职责:定义所有树节点的统一执行方法,所有节点统一具备「解析拼接SQL」的能力。
设计目的:面向接口统一编程,不管是静态文本、if标签、foreach标签,都可以统一遍历执行。
2)IfSqlNode(条件节点)
对应XML标签:<if test="">
核心职责:承载单条件判断逻辑,运行时根据参数判断是否拼接当前节点的SQL内容。
核心逻辑:test表达式为true → 拼接当前SQL;false → 直接跳过当前节点。
3)MixedSqlNode(混合节点)
核心职责:容器节点,专门用来收纳「一堆子SqlNode节点」。
核心场景:一个SQL标签内,同时包含静态文本、<if>、<foreach>、<where> 等多个内容,全部被 MixedSqlNode 统一包裹管理。
设计价值:实现树形层级嵌套,支持无限层级的动态标签组合。
7.4.2 XML标签 → SqlNode 树形结构图(文字完整版)
我们以一段混合动态SQL为例,展示启动时的树形组装过程:
原始XML内容:
SELECT * FROM user <if test="id != null"> AND id = #{id} </if> <if test="name != null"> AND name = #{name} </if>
启动时解析生成SqlNode树结构:
【根节点:MixedSqlNode】
├─ 子节点1:静态文本SqlNode(SELECT * FROM user)
├─ 子节点2:IfSqlNode(id非空条件分支)
└─ 子节点3:IfSqlNode(name非空条件分支)
核心本质:所有动态SQL,最终都会被解析成一棵「多节点嵌套的SqlNode树」
7.5 两大核心引擎类:DynamicSqlSource + BoundSql
SqlNode树是静态结构,真正实现「动态拼接」的核心是这两个类,掌控整个动态SQL的运行逻辑。
7.5.1 DynamicSqlSource:动态SQL数据源(延迟拼接核心)
架构定位:动态SQL的总调度器,所有带动态标签的SQL,启动时都会被封装为 DynamicSqlSource。
核心特性:延迟构建(重中之重)
启动时不拼接SQL:项目启动解析XML,只生成SqlNode树形结构,不执行任何拼接逻辑,不生成最终SQL;
运行时动态拼接:每次接口请求、传入参数不同时,实时遍历SqlNode树,根据当前参数判断分支、拼接出专属SQL。
工作流程:接收运行参数 → 遍历SqlNode树 → 执行if/foreach分支判断 → 实时拼接SQL → 生成BoundSql。
7.5.2 BoundSql:最终可执行SQL载体
架构定位:动态SQL拼接完成后的最终产物,是交给JDBC执行的唯一可执行对象。
内部存储核心信息:
-
拼接完成的完整可执行SQL语句(带?占位符);
-
完整参数映射列表;
-
参数绑定元数据。
核心意义:BoundSql 是 MyBatis 交给 JDBC 执行的「最终成品」,前面所有的SqlNode解析、树形遍历、动态拼接,都是为了生成一个合法的 BoundSql。
7.6 完整动态SQL运行全流程(启动+运行双阶段)
这是动态SQL引擎的完整闭环,分两个阶段,彻底解答「SQL什么时候拼接」的核心问题:
阶段一:项目启动阶段(只建树、不拼接)
-
XMLMapperBuilder 解析 Mapper.xml 文件;
-
识别文件中的 <if>、<foreach> 等动态标签;
-
将静态文本、动态标签逐一解析为对应SqlNode节点;
-
通过 MixedSqlNode 组装成完整SqlNode树;
-
将SqlNode树封装为 DynamicSqlSource,存入 MappedStatement;
-
本阶段无SQL拼接,仅完成树形结构初始化。
阶段二:接口调用运行阶段(实时遍历、动态拼接)
-
用户调用 Mapper 接口方法,传入业务参数;
-
执行链路进入 MappedStatement,获取 DynamicSqlSource;
-
DynamicSqlSource 携带当前业务参数,开始遍历整棵SqlNode树;
-
逐个执行 IfSqlNode 条件判断、循环节点遍历,动态拼接SQL片段;
-
拼接完成,生成完整可执行SQL;
-
封装参数、生成 BoundSql 最终对象;
-
交由 StatementHandler、ParameterHandler 执行后续JDBC流程。
7.7 本章核心设计思想升华(三大顶层思想)
思想1:模板引擎思想
MyBatis 完全复用了「前端模板引擎」的设计思路:模板静态定义 + 数据动态渲染。XML是SQL模板,业务参数是渲染数据,运行时动态渲染出最终SQL,和Vue/FreeMarker模板原理完全一致。
思想2:延迟构建思想(核心性能设计)
为什么不启动时拼接SQL?因为动态SQL的分支、循环逻辑完全依赖运行时参数,启动时无参数、无法预判分支。延迟到运行时拼接,是动态SQL唯一合理的实现方案。
思想3:标签驱动执行
所有动态能力全部由标签驱动扩展,新增动态语法只需新增对应SqlNode实现类,符合开闭原则,无需修改核心引擎源码。
7.8 终极架构结论(颠覆传统认知)
读完本章,彻底纠正认知偏差:
MyBatis 不是传统意义上的 ORM 框架,MyBatis 本质是:轻量级标签驱动SQL模板引擎 + JDBC通用封装工具。
传统ORM框架(Hibernate)是「全自动对象映射,屏蔽SQL」;
而 MyBatis 的核心能力是「接管SQL的模板化、动态化、结构化管理」,让开发者手写SQL更优雅、更易维护、更易扩展。
7.9 本章终极验收标准
读完本章,你可以独立回答所有核心问题:
-
MyBatis 不用SQL AST,改用SqlNode树的设计原因?
-
SqlNode、DynamicSqlSource、BoundSql 三者的层级与协作关系?
-
动态SQL是启动时拼接还是运行时拼接,为什么?
-
MyBatis 动态SQL引擎的核心设计思想是什么?
核心本质:一个超大的结构化存储容器,所有配置、所有SQL、所有映射规则、所有环境参数,全部统一存在这里。
内部核心存储结构(全是Map,键值存储,快速查询):
-
Map<String, MappedStatement> mappedStatements:存储所有SQL语句元数据(核心中的核心);
-
类型别名Map、插件Map、环境配置Map、缓存配置Map、映射关系Map等。
设计目的:
-
中心化管理:把散落的XML配置、SQL语句全部收拢到一个全局容器,统一管控;
-
预加载预解析:项目启动时一次性解析完毕,运行时不再解析XML,提升执行性能;
-
全局可访问:后续 Executor、Handler、插件所有组件,需要配置和SQL时,直接从Configuration获取。
2)XMLConfigBuilder:全局主配置解析器
架构定位:负责解析mybatis-config.xml全局主配置文件的专属解析器。
核心职责:
-
读取全局环境配置(数据库连接、事务、运行参数);
-
解析全局插件、别名、设置项;
-
记录 Mapper 映射文件路径,交给 Mapper 解析器处理。
设计目的:拆分解析职责,专门负责全局框架配置,和业务SQL解析彻底解耦。
3)XMLMapperBuilder:Mapper映射文件解析器
架构定位:专门解析所有 Mapper.xml 文件的专属解析器(业务SQL解析核心)。
核心职责:
-
读取每一个 Mapper.xml 文件内容;
-
解析每一条 select/insert/update/delete 标签;
-
提取 SQL 语句、参数类型、返回值类型、动态标签规则;
-
将每一条 SQL 封装成独立的 MappedStatement 对象;
-
存入 Configuration 的 mappedStatements Map 中。
设计目的:将「业务SQL配置」和「框架全局配置」拆分解析,职责单一、结构清晰、方便扩展。
4)MappedStatement:SQL元数据最小单元
架构定位:MyBatis 中单条SQL的完整元数据载体,是整个配置驱动体系的最终落地产物。
核心本质:把XML中一条SQL的所有信息,结构化、对象化、固化到内存中。
一个 MappedStatement 内部包含所有信息:
-
当前 SQL 的唯一 ID(接口全类名+方法名);
-
原始 SQL 模板语句;
-
参数类型、参数映射规则;
-
返回值类型、ResultMap映射规则;
-
动态SQL标签规则、缓存规则、执行类型。
设计目的:让每一条SQL不再是零散字符串,而是拥有完整属性、可被程序识别、可被拦截修改的结构化对象。
6.5 完整配置解析分步流程(启动阶段全过程)
MyBatis 在项目启动、初始化 SqlSessionFactory 时,会一次性完成所有解析,全程只执行一次:
-
第一步:读取配置文件:加载 mybatis-config.xml 全局配置和所有 Mapper.xml 映射文件;
-
第二步:全局配置解析:XMLConfigBuilder 解析全局环境、参数、插件配置,初始化 Configuration 基础容器;
-
第三步:批量解析Mapper文件:XMLMapperBuilder 遍历所有Mapper文件,逐标签解析SQL;
-
第四步:封装元数据:每解析一条SQL,自动组装成一个 MappedStatement 对象;
-
第五步:全局缓存存储:以「方法全限定名」为Key,MappedStatement为Value,存入Configuration的Map集合;
-
第六步:等待调用:程序运行期间,不再解析XML,需要执行SQL时,直接从Configuration中取出对应MappedStatement执行。
6.6 核心灵魂问题:为什么 XML 本质是「SQL元数据定义语言」?
普通开发者认为:XML 是用来写 SQL 的配置文件。
框架设计者视角:XML 是用来「定义SQL元数据的结构化语言」。
两者的核心区别:
-
普通用法:XML 只是存放SQL字符串;
-
框架设计:XML 定义了SQL语句、参数规则、返回映射、动态规则、缓存规则的全套元数据。
XML 的每一个标签、每一个属性,最终都会被解析成 MappedStatement 的属性,固化到内存。它不是简单的文本配置,是一套专门用来描述数据库操作的领域语言。
6.7 顶层设计思想升华(三大核心思想)
思想1:配置驱动
所有执行行为、SQL规则、映射规则,全部由外部配置定义,而非代码硬编码。修改SQL、修改映射规则无需改Java代码,无需重启核心逻辑,解耦业务与底层。
思想2:元数据化
将零散的SQL字符串,转化为结构化、对象化、可管理、可扩展的元数据对象(MappedStatement),让框架拥有操控SQL的能力。
思想3:运行前解析(核心性能&架构优势)
JDBC:运行时拼接SQL(每次请求都要拼接、易错、低效、无法预校验);
MyBatis:启动时解析SQL(启动一次性解析完毕,运行时直接读取执行,高效、稳定、可统一拦截)。
6.8 本章终极验收
读完本章,你可以清晰回答:
MyBatis 为什么比原生 JDBC 更先进?
因为 MyBatis 把「散落的SQL硬编码」变成了「全局统一管理的结构化元数据」,把「运行时动态拼接」变成了「启动时预解析、运行时直接执行」,实现了可维护、可扩展、可管控、高性能的工程化数据库操作架构。
问题2:MapperProxy 是如何工作的?
MapperProxy 本质是一个方法拦截器,全程只有三步工作:
-
运行时拦截所有Mapper接口的方法调用;
-
解析方法唯一标识(接口名+方法名),匹配对应的SQL元数据;
-
将用户的方法调用,转发给底层的SqlSession执行。
它不处理SQL、不操作数据库、不封装结果,只做「拦截+转发」,职责极度单一。
问题3:整个链路已经有Executor了,为什么还要经过SqlSession?
核心答案:SqlSession 是门面模式,负责「屏蔽复杂性、统一入口、管控生命周期」。
Executor、三大Handler都是底层核心组件,层级极低、逻辑复杂,如果直接暴露给开发者,会极大增加使用难度,还容易出现底层调用错误、资源泄露、事务失控等问题。
SqlSession 封装所有底层细节,对外只提供简单的增删改查方法,同时统一管控数据库会话的生命周期(创建、提交、回滚、关闭),是框架给开发者的唯一安全入口。
问题4:Executor 在整个流程中的核心地位是什么?
Executor 是整条调用链的总指挥、调度核心,属于「大脑层级」组件。
SqlSession 只是门面(对外壳子),真正干活、管控流程、调度组件、管理缓存和事务的核心就是 Executor。
没有Executor,三大Handler各自独立、无法协同工作,整条执行链路会彻底瘫痪。它承上启下,承接上层请求,调度下层所有功能组件,是MyBatis执行层的核心枢纽。
问题5:为什么一定要拆成 StatementHandler / ParameterHandler / ResultSetHandler 三个组件?
核心设计原则:单一职责 + 可插拔扩展 + 彻底解耦。
原生JDBC将「语句创建、参数赋值、结果封装」揉在一块,耦合严重、无法单独扩展。MyBatis将其拆分为三个独立接口,每一个组件只做一件事:
-
StatementHandler:只管SQL语句的创建和执行;
-
ParameterHandler:只管参数绑定和类型转换;
-
ResultSetHandler:只管结果集和对象映射。
拆分后的最大优势:可单独替换、可单独拦截、可单独扩展。后续MyBatis的插件机制(分页、日志、多租户),全部依赖这三个独立组件的可扩展能力实现。如果不拆分,所有扩展都需要修改核心源码,完全不具备工程化能力。
5.5 整条调用链的顶层设计思想总结
整条 MyBatis 调用链,本质是一套分层解耦、职责单一、可插拔扩展的工程化架构:
-
上层门面屏蔽底层复杂度:SqlSession、MapperProxy 屏蔽所有底层细节,简化开发者使用;
-
中层统一调度:Executor 统一管控全流程,保证执行秩序;
-
底层单一职责拆分:三大Handler各司其职,支持无限扩展;
-
全程无硬编码耦合:所有层级基于接口依赖,组件可替换、可拦截、可拓展。
这也是为什么 MyBatis 灵活、稳定、可扩展,远超原生JDBC的核心架构优势。
5.6 专属源码阅读指引(精准定向,不看冗余代码)
按照设计者视角,本章只需要阅读核心类的接口定义与核心方法,无需深入复杂实现,新手友好:
-
MapperProxy.java:重点看 invoke() 拦截方法,理解代理拦截、请求转发逻辑;
-
DefaultSqlSession.java:重点看 selectOne/update/insert/delete 通用方法,理解门面调度逻辑;
-
SimpleExecutor.java:重点看 doQuery 核心执行方法,理解组件调度、SQL执行总流程。
源码地址:MyBatis GitHub源码
5.7 本章终极验收标准
读完本章,你可以不看任何资料,完整手写如下执行流程:
userMapper.selectById(1) → MapperProxy拦截 → SqlSession接收调度 → Executor全局调度 → StatementHandler创建SQL语句 → ParameterHandler绑定参数 → JDBC执行SQL → ResultSetHandler反射封装对象 → 返回实体
更多推荐

所有评论(0)