前言

绝大多数人学习 MyBatis,都停留在「会用」的层面:会写 XML、会写 Mapper 接口、会做 CRUD 业务开发。但本套课程完全跳出使用层,站在框架设计者的角度,带你搞懂 MyBatis 最核心的底层根基——顶层接口抽象设计。

这是 MyBatis 整个架构的起点和根基,所有的插件机制、动态 SQL、执行流程、配置驱动,全部都是基于这一套顶层接口延伸出来的。

学完本章,你彻底告别「只会用框架」的普通开发者,拥有框架设计思维,能回答出所有高级面试核心问题:MyBatis 为什么要拆分这五大核心接口?为什么不直接使用原生 JDBC?MyBatis 的分层解耦思想到底是什么?

本章严格约束:全程不讲解 XML 配置、不讲解动态 SQL 使用、不讲解 Mapper 开发、不讲解业务实操,只讲架构分层、接口设计、底层思想。

一、本章核心学习目标(必看)

读完本章,你将彻底理解 5 个核心底层问题,做到零基础吃透 MyBatis 顶层架构:

  1. 原生 JDBC 存在哪些致命缺陷,迫使 MyBatis 必须重新设计框架?

  2. MyBatis 为什么不直接复用 JDBC 原生 API,非要自定义一套全新的接口体系?

  3. 五大核心接口(Executor、StatementHandler、ParameterHandler、ResultSetHandler、SqlSession)的拆分逻辑是什么?是随意拆分还是遵循严格设计原则?

  4. SqlSession 门面模式的设计意义,为什么禁止用户直接操作底层执行器?

  5. 五大接口的职责边界、层级关系、依赖关系,彻底搞懂 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 接口的所有方法调用。

核心工作逻辑

  1. 开发者调用接口方法时,因为接口无实现类,所有请求会被 MapperProxy 代理对象拦截;

  2. 提取当前调用的接口全限定名 + 方法名,精准匹配全局缓存中的 MappedStatement(SQL元数据);

  3. 将拦截到的方法请求,统一转发给 SqlSession 处理。

设计目的:这就是「为什么不用写DAO实现类」的核心答案!通过动态代理自动生成实现逻辑,省去重复的DAO实现类开发,同时统一拦截所有Mapper请求,实现全局统一管控。

第三步:SqlSession 门面调度(统一入口层)

对应核心源码类org.apache.ibatis.session.defaults.DefaultSqlSession

执行职责:MyBatis 统一门面入口,承接所有代理转发的数据库请求。

核心工作逻辑

  1. 接收 MapperProxy 转发的方法请求;

  2. 根据方法信息匹配对应的 SQL 元数据;

  3. 将请求下发给底层真正的执行器 Executor。

设计目的:门面模式核心体现!屏蔽底层所有复杂组件(Executor、三大Handler),对外提供统一、简洁的调用入口,避免用户直接操作底层核心组件,降低使用门槛、规避底层操作风险。

第四步:Executor 全局执行调度(核心总指挥)

对应核心源码类org.apache.ibatis.executor.SimpleExecutor(默认简单执行器)

执行职责:整条链路的总调度、总管控,是MyBatis执行层的核心枢纽。

核心工作逻辑

  1. 接收 SqlSession 下发的执行请求;

  2. 管控数据库连接、事务、一级/二级缓存;

  3. 根据执行需求,调度 StatementHandler、ParameterHandler、ResultSetHandler 三大组件依次工作;

  4. 兜底管控整条SQL执行全流程。

设计目的:统一调度底层所有执行组件,实现执行流程统一管控、缓存统一管理、事务统一控制,让三大Handler只专注自身单一职责。

第五步:StatementHandler SQL语句处理(语句管理层)

对应核心源码类org.apache.ibatis.executor.statement.StatementHandler

执行职责:专门负责JDBC语句对象的创建与SQL执行。

核心工作逻辑

  1. 接收Executor调度指令;

  2. 创建JDBC原生 PreparedStatement 预处理对象;

  3. 定义最终要执行的SQL语句模板;

  4. 等待参数绑定完成后,执行SQL语句。

设计目的:单独拆分SQL语句创建、执行、生命周期管理逻辑,和参数处理、结果处理彻底解耦,支持单独扩展SQL执行逻辑。

第六步:ParameterHandler 参数绑定(参数处理层)

对应核心源码类org.apache.ibatis.executor.parameter.ParameterHandler

执行职责:专门负责SQL占位符的参数赋值、类型转换。

核心工作逻辑

  1. 解析方法传入的参数数据;

  2. 自动完成Java类型→数据库类型的转换;

  3. 给PreparedStatement的SQL占位符逐一赋值,补全完整可执行SQL。

设计目的:将繁琐、易变的参数处理逻辑单独抽离,统一参数绑定规则,支持自定义参数加密、脱敏、类型转换等扩展功能。

第七步:JDBC 原生执行(底层落地层)

对应核心源码:JDBC原生API

执行职责:执行完整SQL语句,访问数据库,获取原始结果集 ResultSet。

核心工作逻辑:调用原生JDBC方法,向数据库发起SQL请求,数据库执行后返回原始数据结果集。

设计目的:MyBatis 不重构数据库底层通信逻辑,仅做上层封装,保证底层稳定性,同时完全屏蔽原生JDBC的繁琐操作。

第八步:ResultSetHandler 结果封装(数据返回层)

对应核心源码类org.apache.ibatis.executor.resultset.ResultSetHandler

执行职责:专门负责数据库原始结果集→Java实体对象的自动映射。

核心工作逻辑

  1. 接收JDBC返回的原始ResultSet结果集;

  2. 通过Java反射机制,读取结果集字段;

  3. 自动封装、映射为开发者定义的POJO实体对象;

  4. 关闭资源,返回封装后的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 完整调用链,本质是一套分层解耦、逐层委派、职责单一、可无限扩展的工程化执行架构:

  1. 代理层解耦:MapperProxy 消除 DAO 实现类,简化开发;

  2. 门面层屏蔽:SqlSession 屏蔽底层复杂度,统一入口;

  3. 执行层调度:Executor 统一管控全流程,保证执行秩序;

  4. 组件层拆分:三大 Handler 各司其职,预留扩展点;

  5. 底层复用:基于原生 JDBC 执行,保证稳定可靠。

六、MyBatis 核心架构:配置驱动体系(设计者视角·核心进阶)

前面两章我们搞定了「顶层接口抽象」和「运行时调用链路」,解决了MyBatis 怎么执行的问题。本章我们解决一个更底层的问题:MyBatis 的 SQL 从哪里来?为什么不用运行时字符串拼接?什么是配置驱动?

本章是 MyBatis 脱离 JDBC 硬编码、成为现代化框架的核心分水岭。读完本章,你将彻底理解:MyBatis 不是在运行时拼 SQL,而是启动时预解析 SQL、元数据固化、运行时直接执行的高级设计思想。

6.1 本章核心学习目标

零基础吃透 MyBatis 配置驱动核心架构,彻底理解四大核心问题:

  1. Configuration 全局容器的架构定位与内部存储结构;

  2. XML 配置文件如何被一步步解析、加载、转化为程序可识别数据;

  3. MappedStatement 的核心作用,为什么它是 SQL 的元数据载体;

  4. 为什么说 XML 不是配置文件,而是「SQL 元数据定义语言」。

最终能力:清晰说出 MyBatis「启动预解析、运行直接执行」的核心优势,区别于原生 JDBC 运行时拼接字符串的弊端。

6.2 前置认知:JDBC 硬编码的终极痛点

原生 JDBC 除了代码耦合、冗余之外,还有一个致命工程化缺陷:SQL 是运行时动态字符串,无统一管理、无预校验、无结构化存储

原生 JDBC 的问题:

  1. SQL 散落嵌套在业务代码中,无法统一维护;

  2. 所有 SQL 都是运行时拼接,容易出现语法错误、注入漏洞;

  3. 没有统一的元数据记录,无法统一拦截、修改、监控 SQL;

  4. 参数、返回值、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等。

设计目的

  1. 中心化管理:把散落的XML配置、SQL语句全部收拢到一个全局容器,统一管控;

  2. 预加载预解析:项目启动时一次性解析完毕,运行时不再解析XML,提升执行性能;

  3. 全局可访问:后续 Executor、Handler、插件所有组件,需要配置和SQL时,直接从Configuration获取。

2)XMLConfigBuilder:全局主配置解析器

架构定位:负责解析mybatis-config.xml全局主配置文件的专属解析器。

核心职责

  1. 读取全局环境配置(数据库连接、事务、运行参数);

  2. 解析全局插件、别名、设置项;

  3. 记录 Mapper 映射文件路径,交给 Mapper 解析器处理。

设计目的:拆分解析职责,专门负责全局框架配置,和业务SQL解析彻底解耦。

3)XMLMapperBuilder:Mapper映射文件解析器

架构定位:专门解析所有 Mapper.xml 文件的专属解析器(业务SQL解析核心)。

核心职责

  1. 读取每一个 Mapper.xml 文件内容;

  2. 解析每一条 select/insert/update/delete 标签;

  3. 提取 SQL 语句、参数类型、返回值类型、动态标签规则;

  4. 将每一条 SQL 封装成独立的 MappedStatement 对象;

  5. 存入 Configuration 的 mappedStatements Map 中。

设计目的:将「业务SQL配置」和「框架全局配置」拆分解析,职责单一、结构清晰、方便扩展。

4)MappedStatement:SQL元数据最小单元

架构定位:MyBatis 中单条SQL的完整元数据载体,是整个配置驱动体系的最终落地产物。

核心本质:把XML中一条SQL的所有信息,结构化、对象化、固化到内存中

一个 MappedStatement 内部包含所有信息

  • 当前 SQL 的唯一 ID(接口全类名+方法名);

  • 原始 SQL 模板语句;

  • 参数类型、参数映射规则;

  • 返回值类型、ResultMap映射规则;

  • 动态SQL标签规则、缓存规则、执行类型。

设计目的:让每一条SQL不再是零散字符串,而是拥有完整属性、可被程序识别、可被拦截修改的结构化对象。

6.5 完整配置解析分步流程(启动阶段全过程)

MyBatis 在项目启动、初始化 SqlSessionFactory 时,会一次性完成所有解析,全程只执行一次:

  1. 第一步:读取配置文件:加载 mybatis-config.xml 全局配置和所有 Mapper.xml 映射文件;

  2. 第二步:全局配置解析:XMLConfigBuilder 解析全局环境、参数、插件配置,初始化 Configuration 基础容器;

  3. 第三步:批量解析Mapper文件:XMLMapperBuilder 遍历所有Mapper文件,逐标签解析SQL;

  4. 第四步:封装元数据:每解析一条SQL,自动组装成一个 MappedStatement 对象;

  5. 第五步:全局缓存存储:以「方法全限定名」为Key,MappedStatement为Value,存入Configuration的Map集合;

  6. 第六步:等待调用:程序运行期间,不再解析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 本章核心学习目标

读完本章,彻底吃透四大核心底层问题:

  1. MyBatis 到底有没有使用专业 SQL 语法树(AST)?官方设计取舍是什么?

  2. SqlNode 树形结构的本质是什么,如何承载所有动态标签逻辑?

  3. DynamicSqlSource 核心工作原理,如何实现动态SQL延迟拼接?

  4. 动态SQL的真正拼接时机,为什么是运行时拼接而非启动时?

最终认知升级:彻底理解 MyBatis = 轻量级SQL模板引擎 + JDBC封装,并非传统全功能ORM框架。

7.2 前置痛点:原生动态SQL的致命问题

在没有模板引擎之前,Java实现动态查询只能硬编码字符串拼接:

  • 判断参数非空,手动拼接whereandor

  • 循环遍历参数,手动拼接 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 数据源:

  1. StaticSqlSource:无任何动态标签,启动时直接拼接完成SQL,运行时直接执行;

  2. DynamicSqlSource:包含if/foreach等动态标签,启动时只建树、不拼接,运行时根据参数动态渲染

7.5.2 为什么动态SQL必须运行时拼接?

核心答案:动态分支、循环次数完全依赖用户传入的运行时参数,启动时无任何入参,无法预判SQL最终形态

这是「延迟构建」的核心设计思想:固定模板提前预编译,动态数据运行时填充,兼顾启动性能与动态灵活性。

7.6 最终产物:BoundSql(可执行SQL载体)

DynamicSqlSource 遍历 SqlNode 树、执行所有条件判断与循环渲染后,最终生成BoundSql 对象,这是交给JDBC执行的唯一成品。

BoundSql 内部封装三大核心信息:

  1. 完整渲染后的可执行SQL(带 ? 占位符);

  2. 完整参数映射列表与参数元数据;

  3. 动态渲染后的最终语句结构。

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 插件架构,彻底掌握四大核心问题:

  1. Interceptor 拦截器的核心定位与工作原理;

  2. MyBatis 仅允许拦截4个核心接口的底层设计原因;

  3. Plugin 类如何通过JDK动态代理实现对象包装与拦截;

  4. 多插件场景下,责任链+动态代理的协同执行逻辑。

最终能力:彻底理解插件如何拦截、修改、改写、增强原生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 严格限制插件拦截范围,全局只允许拦截四大顶层接口,无任何其他扩展点,这是框架刻意的架构约束。

四大可拦截目标(完全对应前文五大核心接口)

  1. Executor:全局执行器(拦截SQL执行、事务、缓存全过程);

  2. StatementHandler:SQL语句处理器(拦截SQL创建、执行,最常用扩展点,分页插件核心依赖);

  3. ParameterHandler:参数处理器(拦截参数绑定、参数加密、脱敏);

  4. ResultSetHandler:结果集处理器(拦截结果封装、数据脱敏、字段转换)。

8.4.1 灵魂问题:为什么只允许拦截这4个接口?

这是框架设计者的极致架构权衡,核心4点原因:

  1. 分层边界清晰:这4个接口是MyBatis执行层的唯一核心组件,职责单一、层级稳定,是所有SQL执行的必经链路;

  2. 可控扩展、防止滥用:如果开放所有类拦截,开发者可随意篡改框架任意逻辑,会导致架构混乱、版本不兼容、线上隐患;

  3. 保证内核稳定:框架核心内核(配置解析、动态SQL引擎)不允许被拦截,保证底层基础能力绝对稳定;

  4. 覆盖所有扩展场景:四大扩展点已经覆盖「执行、SQL、参数、结果」全流程,足以实现99%的业务扩展需求(分页、日志、多租户、脱敏、加密)。

核心总结:MyBatis 插件是有限制的可扩展,而非无限制随意篡改,兼顾扩展性与稳定性。

8.5 核心机制:Plugin 动态代理包装原理

插件能够实现拦截的核心底层支撑:Plugin.wrap() 动态代理包装机制

所有四大核心组件(Executor/Handler)创建后,不会直接原生使用,而是经过 Plugin 包装,生成代理对象

8.5.1 Plugin 核心工作流程
  1. 目标对象初始化:MyBatis 创建原生 Executor、StatementHandler 等核心对象;

  2. Plugin.wrap 包装:检测当前是否存在自定义插件,若存在,通过JDK动态代理生成目标对象的代理类;

  3. 代理拦截方法:后续所有原生对象的方法调用,都会被Plugin代理拦截;

  4. 执行插件逻辑:优先执行自定义Interceptor的intercept增强逻辑,再执行原生方法,实现无侵入增强。

8.5.2 关键设计:JDK 动态代理适配

MyBatis 四大拦截目标全部是接口,完美适配JDK动态代理(仅支持接口代理),无需依赖CGLIB,保证框架轻量、无第三方依赖、高性能。

8.6 插件完整拦截流程图(必背文本版)

原生核心对象初始化 → Plugin.wrap() 生成代理对象 → 业务调用触发方法执行 → 进入Interceptor拦截方法(自定义增强逻辑) → 执行原生目标方法 → 后置增强处理 → 返回结果

8.7 多插件协同:责任链 + 动态代理嵌套机制

当项目中存在多个插件(如分页插件+日志插件)时,MyBatis 采用多层代理嵌套的责任链模式执行。

执行顺序规则

  1. 插件配置顺序靠前的,先包装、后执行、最后结束

  2. 多层代理层层嵌套,形成链式拦截,互不干扰、独立扩展;

  3. 所有插件遵循「前置增强→原生执行→后置增强」的统一秩序。

核心优势:插件完全可插拔,新增/删除插件无需修改任何业务代码、无需改动核心源码。

8.8 经典实战案例(架构层面解析,不讲用法)

案例1:分页插件(最经典插件设计)

拦截目标:StatementHandler

架构原理

  1. 拦截 StatementHandler 的 SQL 预处理方法;

  2. 在原生SQL执行前,拦截原始查询语句;

  3. 根据分页参数,动态改写SQL,拼接 limit 分页语句;

  4. 执行改写后的分页SQL,实现无侵入分页。

核心价值:不修改Mapper XML、不改动业务代码,通过插件直接改变SQL执行内容

案例2:SQL日志打印插件

拦截目标:Executor

架构原理

  1. 拦截Executor全局执行方法,在SQL执行前后做增强;

  2. 前置打印:完整可执行SQL、参数信息、执行耗时;

  3. 后置打印:执行结果、影响行数;

  4. 不修改任何原生执行逻辑,仅做监控增强。

8.9 本章核心顶层设计思想升华

思想1:自研轻量化AOP思想

不依赖Spring AOP,框架内置AOP能力,专注数据库执行链路增强,轻量、高效、无冗余。

思想2:可插拔架构

所有扩展功能全部基于插件实现,插件可随时新增、移除、替换,核心业务与扩展功能完全解耦,符合开闭原则

思想3:可控扩展点设计

框架主动收敛扩展点,只开放四大核心接口,既保证足够的扩展能力,又杜绝无限制篡改内核逻辑,实现扩展性与稳定性的完美平衡

8.10 终极架构结论

MyBatis 插件可以改变SQL执行流程的核心原因

插件通过 Plugin动态代理 + Interceptor拦截机制,在SQL执行的核心节点(SQL创建、参数绑定、语句执行、结果封装)进行前置拦截,可修改SQL语句、修改参数、修改执行逻辑、修改返回结果,全程无侵入、无源码修改,这就是MyBatis可扩展架构的终极内核。

8.11 本章验收标准

读完本章,你可以独立回答:

  1. MyBatis 插件与Spring AOP的区别?

  2. 为什么框架只允许拦截四大核心接口?

  3. Plugin.wrap 动态代理的核心工作流程?

  4. 分页插件能够改写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 树?

  1. 规避复杂度爆炸:标准SQL AST需要兼容海量SQL语法、版本、方言,开发维护成本极高;

  2. 按需设计、够用即可:MyBatis只需要实现「条件分支、循环、文本拼接」基础模板能力,无需语法校验、SQL优化;

  3. 追求轻量高效:自定义SqlNode树体量极小、解析速度快、无冗余逻辑;

  4. 聚焦职责单一: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什么时候拼接」的核心问题:

阶段一:项目启动阶段(只建树、不拼接)
  1. XMLMapperBuilder 解析 Mapper.xml 文件;

  2. 识别文件中的 <if>、<foreach> 等动态标签;

  3. 将静态文本、动态标签逐一解析为对应SqlNode节点;

  4. 通过 MixedSqlNode 组装成完整SqlNode树;

  5. 将SqlNode树封装为 DynamicSqlSource,存入 MappedStatement;

  6. 本阶段无SQL拼接,仅完成树形结构初始化

阶段二:接口调用运行阶段(实时遍历、动态拼接)
  1. 用户调用 Mapper 接口方法,传入业务参数;

  2. 执行链路进入 MappedStatement,获取 DynamicSqlSource;

  3. DynamicSqlSource 携带当前业务参数,开始遍历整棵SqlNode树;

  4. 逐个执行 IfSqlNode 条件判断、循环节点遍历,动态拼接SQL片段;

  5. 拼接完成,生成完整可执行SQL;

  6. 封装参数、生成 BoundSql 最终对象;

  7. 交由 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 本章终极验收标准

读完本章,你可以独立回答所有核心问题:

  1. MyBatis 不用SQL AST,改用SqlNode树的设计原因?

  2. SqlNode、DynamicSqlSource、BoundSql 三者的层级与协作关系?

  3. 动态SQL是启动时拼接还是运行时拼接,为什么?

  4. MyBatis 动态SQL引擎的核心设计思想是什么?

核心本质:一个超大的结构化存储容器,所有配置、所有SQL、所有映射规则、所有环境参数,全部统一存在这里。

内部核心存储结构(全是Map,键值存储,快速查询)

  • Map<String, MappedStatement> mappedStatements:存储所有SQL语句元数据(核心中的核心);

  • 类型别名Map、插件Map、环境配置Map、缓存配置Map、映射关系Map等。

设计目的

  1. 中心化管理:把散落的XML配置、SQL语句全部收拢到一个全局容器,统一管控;

  2. 预加载预解析:项目启动时一次性解析完毕,运行时不再解析XML,提升执行性能;

  3. 全局可访问:后续 Executor、Handler、插件所有组件,需要配置和SQL时,直接从Configuration获取。

2)XMLConfigBuilder:全局主配置解析器

架构定位:负责解析mybatis-config.xml全局主配置文件的专属解析器。

核心职责

  1. 读取全局环境配置(数据库连接、事务、运行参数);

  2. 解析全局插件、别名、设置项;

  3. 记录 Mapper 映射文件路径,交给 Mapper 解析器处理。

设计目的:拆分解析职责,专门负责全局框架配置,和业务SQL解析彻底解耦。

3)XMLMapperBuilder:Mapper映射文件解析器

架构定位:专门解析所有 Mapper.xml 文件的专属解析器(业务SQL解析核心)。

核心职责

  1. 读取每一个 Mapper.xml 文件内容;

  2. 解析每一条 select/insert/update/delete 标签;

  3. 提取 SQL 语句、参数类型、返回值类型、动态标签规则;

  4. 将每一条 SQL 封装成独立的 MappedStatement 对象;

  5. 存入 Configuration 的 mappedStatements Map 中。

设计目的:将「业务SQL配置」和「框架全局配置」拆分解析,职责单一、结构清晰、方便扩展。

4)MappedStatement:SQL元数据最小单元

架构定位:MyBatis 中单条SQL的完整元数据载体,是整个配置驱动体系的最终落地产物。

核心本质:把XML中一条SQL的所有信息,结构化、对象化、固化到内存中

一个 MappedStatement 内部包含所有信息

  • 当前 SQL 的唯一 ID(接口全类名+方法名);

  • 原始 SQL 模板语句;

  • 参数类型、参数映射规则;

  • 返回值类型、ResultMap映射规则;

  • 动态SQL标签规则、缓存规则、执行类型。

设计目的:让每一条SQL不再是零散字符串,而是拥有完整属性、可被程序识别、可被拦截修改的结构化对象。

6.5 完整配置解析分步流程(启动阶段全过程)

MyBatis 在项目启动、初始化 SqlSessionFactory 时,会一次性完成所有解析,全程只执行一次:

  1. 第一步:读取配置文件:加载 mybatis-config.xml 全局配置和所有 Mapper.xml 映射文件;

  2. 第二步:全局配置解析:XMLConfigBuilder 解析全局环境、参数、插件配置,初始化 Configuration 基础容器;

  3. 第三步:批量解析Mapper文件:XMLMapperBuilder 遍历所有Mapper文件,逐标签解析SQL;

  4. 第四步:封装元数据:每解析一条SQL,自动组装成一个 MappedStatement 对象;

  5. 第五步:全局缓存存储:以「方法全限定名」为Key,MappedStatement为Value,存入Configuration的Map集合;

  6. 第六步:等待调用:程序运行期间,不再解析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 本质是一个方法拦截器,全程只有三步工作:

  1. 运行时拦截所有Mapper接口的方法调用;

  2. 解析方法唯一标识(接口名+方法名),匹配对应的SQL元数据;

  3. 将用户的方法调用,转发给底层的SqlSession执行。

它不处理SQL、不操作数据库、不封装结果,只做「拦截+转发」,职责极度单一。

问题3:整个链路已经有Executor了,为什么还要经过SqlSession?

核心答案:SqlSession 是门面模式,负责「屏蔽复杂性、统一入口、管控生命周期」

Executor、三大Handler都是底层核心组件,层级极低、逻辑复杂,如果直接暴露给开发者,会极大增加使用难度,还容易出现底层调用错误、资源泄露、事务失控等问题。

SqlSession 封装所有底层细节,对外只提供简单的增删改查方法,同时统一管控数据库会话的生命周期(创建、提交、回滚、关闭),是框架给开发者的唯一安全入口

问题4:Executor 在整个流程中的核心地位是什么?

Executor 是整条调用链的总指挥、调度核心,属于「大脑层级」组件。

SqlSession 只是门面(对外壳子),真正干活、管控流程、调度组件、管理缓存和事务的核心就是 Executor。

没有Executor,三大Handler各自独立、无法协同工作,整条执行链路会彻底瘫痪。它承上启下,承接上层请求,调度下层所有功能组件,是MyBatis执行层的核心枢纽。

问题5:为什么一定要拆成 StatementHandler / ParameterHandler / ResultSetHandler 三个组件?

核心设计原则:单一职责 + 可插拔扩展 + 彻底解耦

原生JDBC将「语句创建、参数赋值、结果封装」揉在一块,耦合严重、无法单独扩展。MyBatis将其拆分为三个独立接口,每一个组件只做一件事:

  1. StatementHandler:只管SQL语句的创建和执行;

  2. ParameterHandler:只管参数绑定和类型转换;

  3. ResultSetHandler:只管结果集和对象映射。

拆分后的最大优势:可单独替换、可单独拦截、可单独扩展。后续MyBatis的插件机制(分页、日志、多租户),全部依赖这三个独立组件的可扩展能力实现。如果不拆分,所有扩展都需要修改核心源码,完全不具备工程化能力。

5.5 整条调用链的顶层设计思想总结

整条 MyBatis 调用链,本质是一套分层解耦、职责单一、可插拔扩展的工程化架构:

  1. 上层门面屏蔽底层复杂度:SqlSession、MapperProxy 屏蔽所有底层细节,简化开发者使用;

  2. 中层统一调度:Executor 统一管控全流程,保证执行秩序;

  3. 底层单一职责拆分:三大Handler各司其职,支持无限扩展;

  4. 全程无硬编码耦合:所有层级基于接口依赖,组件可替换、可拦截、可拓展。

这也是为什么 MyBatis 灵活、稳定、可扩展,远超原生JDBC的核心架构优势。

5.6 专属源码阅读指引(精准定向,不看冗余代码)

按照设计者视角,本章只需要阅读核心类的接口定义与核心方法,无需深入复杂实现,新手友好:

  1. MapperProxy.java:重点看 invoke() 拦截方法,理解代理拦截、请求转发逻辑;

  2. DefaultSqlSession.java:重点看 selectOne/update/insert/delete 通用方法,理解门面调度逻辑;

  3. SimpleExecutor.java:重点看 doQuery 核心执行方法,理解组件调度、SQL执行总流程。

源码地址:MyBatis GitHub源码

5.7 本章终极验收标准

读完本章,你可以不看任何资料,完整手写如下执行流程

userMapper.selectById(1) → MapperProxy拦截 → SqlSession接收调度 → Executor全局调度 → StatementHandler创建SQL语句 → ParameterHandler绑定参数 → JDBC执行SQL → ResultSetHandler反射封装对象 → 返回实体

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐