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 执行。

一次查询的执行链

典型查询流程如下:

  1. SqlSession#getMapper() 创建 Mapper 代理。
  2. 代理定位 MappedStatement。
  3. SqlSession 委托 Executor 查询。
  4. Executor 处理一级缓存并创建 StatementHandler。
  5. ParameterHandler 设置 SQL 参数。
  6. JDBC 执行 SQL。
  7. ResultSetHandler 将结果映射为 Java 对象。

二级缓存位于 namespace 级别,跨 SqlSession 生效;一级缓存默认属于同一个 SqlSession。更新操作和事务边界会影响缓存有效性。

源码阅读入口

  • SqlSessionFactoryBuilder:配置解析入口。
  • XMLConfigBuilderXMLMapperBuilder:配置和 Mapper XML 解析。
  • DefaultSqlSession:面向调用者的会话门面。
  • MapperProxy:Mapper 接口动态代理。
  • Executor:执行与缓存主流程。
  • StatementHandler:JDBC Statement 生命周期。
  • ParameterHandlerResultSetHandler:输入输出映射。

MyBatis 插件通过动态代理拦截 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 的特定方法。分页、审计和 SQL 改写通常建立在这个扩展机制上。

总结

MyBatis 的架构是对 JDBC 变化点的逐层抽象:Configuration 管元数据,MappedStatement 描述 SQL,MapperProxy 把接口调用转成语句调用,Executor 组织执行与缓存,Handler 体系处理 JDBC 细节。沿着一次查询链阅读源码,比按包逐类阅读更容易建立整体认识。