2026年7月13日 · 3 分钟阅读
手写 MyBatis:从 JDBC 到 Mapper 动态代理与执行器
通过四版手写框架理解配置解析、SqlSession、MapperProxy、Executor、StatementHandler、参数映射、结果映射及插件机制。
MyBatis 的价值,是在 JDBC 之上管理 SQL、参数、连接、执行与结果映射,同时允许开发者保留对 SQL 的控制。手写过程适合分四个阶段推进。
V1:封装原始 JDBC
原始 JDBC 的固定步骤包括获取连接、创建 Statement、设置参数、执行 SQL、遍历结果集和释放资源。第一版只需把这些模板代码集中起来,先确认哪些部分固定、哪些部分变化。
变化点主要有:SQL、参数列表、返回类型和字段映射。
V2:面向过程的通用执行
把 SQL 和参数作为输入,把 ResultSet 映射为目标对象。此时可以形成一个简单的查询函数,但 SQL 仍散落在 Java 代码中,调用方也必须知道底层执行细节。
V3:引入配置文件
将连接信息和 SQL 映射移到配置文件,解析后形成内存模型:
- Configuration 保存全局配置。
- MappedStatement 保存语句 ID、SQL、参数类型和结果类型。
- statementId 唯一定位一条 SQL。
这一步完成了“代码与 SQL 解耦”,已经接近 MyBatis 的基本轮廓。
V4:面向对象与 Mapper 代理
最终可以拆出这些核心角色:
SqlSessionFactory
-> SqlSession
-> Executor
-> StatementHandler
-> ParameterHandler
-> ResultSetHandler
Mapper 接口没有实现类,却能执行 SQL,是因为 MapperProxy 使用 JDK 动态代理拦截方法调用,将“接口全限定名 + 方法名”转换为 statementId,再交给 SqlSession 执行。
一次查询的执行链
典型查询流程如下:
SqlSession#getMapper()创建 Mapper 代理。- 代理定位 MappedStatement。
- SqlSession 委托 Executor 查询。
- Executor 处理一级缓存并创建 StatementHandler。
- ParameterHandler 设置 SQL 参数。
- JDBC 执行 SQL。
- ResultSetHandler 将结果映射为 Java 对象。
二级缓存位于 namespace 级别,跨 SqlSession 生效;一级缓存默认属于同一个 SqlSession。更新操作和事务边界会影响缓存有效性。
源码阅读入口
SqlSessionFactoryBuilder:配置解析入口。XMLConfigBuilder与XMLMapperBuilder:配置和 Mapper XML 解析。DefaultSqlSession:面向调用者的会话门面。MapperProxy:Mapper 接口动态代理。Executor:执行与缓存主流程。StatementHandler:JDBC Statement 生命周期。ParameterHandler、ResultSetHandler:输入输出映射。
MyBatis 插件通过动态代理拦截 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 的特定方法。分页、审计和 SQL 改写通常建立在这个扩展机制上。
总结
MyBatis 的架构是对 JDBC 变化点的逐层抽象:Configuration 管元数据,MappedStatement 描述 SQL,MapperProxy 把接口调用转成语句调用,Executor 组织执行与缓存,Handler 体系处理 JDBC 细节。沿着一次查询链阅读源码,比按包逐类阅读更容易建立整体认识。