当前位置: 首页 > news >正文

MyBatis的工作流程及源码连贯阅读方式

MyBatis 的工作流程可概括为以下核心步骤:

  1. 加载配置
    读取全局配置文件(mybatis-config.xml),解析数据源、事务管理器、映射文件(mapper.xml)或注解配置。

  2. 创建 SqlSessionFactory
    使用配置信息构建SqlSessionFactory对象(通常通过SqlSessionFactoryBuilder),该对象是应用全局单例。

  3. 获取 SqlSession
    SqlSessionFactory中打开一个SqlSession,代表一次数据库会话,提供执行 SQL 的方法。

  4. 获取 Mapper 代理对象
    通过SqlSession.getMapper(Class)为映射器接口生成动态代理对象,代理内部调用SqlSession的 SQL 执行方法。

  5. 执行 SQL
    调用 Mapper 接口中的方法,代理底层会根据方法签名和参数,通过SqlSession找到对应 SQL 语句(依据命名空间和 ID),并设置参数(如#{param}占位符)后交给 JDBC 执行。

  6. 结果映射
    JDBC 返回结果集后,MyBatis 自动将结果集映射为 Java 对象(支持简单类型、Map、POJO 等),并返回给调用方。

  7. 事务控制与资源关闭
    业务代码中可显式提交/回滚事务,最后关闭SqlSession(释放数据库连接)。

核心思想:将 SQL 与 Java 代码解耦,通过动态代理和配置映射,简化 JDBC 操作。


连贯的源码阅读,总体思路:从 API 调用入手,逐层跟进去

阅读框架源码最好的方式,是从一个最简单的可运行的 API 调用示例入手,在 IDE 中开启 Debug 模式,逐层跟进去。结合Mybatis3.5.x版本源码,以这段经典代码为起点:

Stringresource="mybatis-config.xml";InputStreaminputStream=Resources.getResourceAsStream(resource);// ① 构建 SqlSessionFactorySqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(inputStream);// ② 打开 SqlSessiontry(SqlSessionsession=sqlSessionFactory.openSession()){// ③ 获取 Mapper 代理对象BusinessMappermapper=session.getMapper(BusinessMapper.class);// ④ 执行 SQL 查询(从这里开始 Debug 进入)⭐Businessbusiness=mapper.selectBusinessById(1);System.out.println(business);}

调试入口点:在mapper.selectBusinessById(1)这一行打上断点,然后 Debug 运行,IDE 会进入到 MapperProxy 等核心类中。

三大阶段,完整调用链路

MyBatis 的源码执行路径可以分成三个连贯的大阶段,各个阶段各自有清晰的入口和调用链。

第一阶段:启动与初始化 —— 入口SqlSessionFactoryBuilder.build()

这是整个框架的"建房子"阶段,把配置文件变成内存中的配置模型。

入口类SqlSessionFactoryBuilder,它使用了建造者模式,有 9 个重载的build()方法,用以不同的方式创建工厂对象。

核心调用链追踪路线(按顺序打断点即可):

  1. SqlSessionFactoryBuilder.build(InputStream)

    • 这是用户代码中调用的第一个方法,创建 XMLConfigBuilder 对象。
  2. XMLConfigBuilder.parse()

    • 先检查是否已被解析过,然后调用parseConfiguration()方法。
  3. XMLConfigBuilder.parseConfiguration(XNode)(重点关注 🔥)

    • 这里按照 MyBatis 全局配置文件的节点顺序,逐一解析每个标签:<properties><settings><typeAliases><plugins><environments><mappers>等。
    • 每解析一个标签,就把解析结果存入Configuration对象中。
  4. XMLMapperBuilder.parse()

    • 当遇到<mappers>标签时,会进入到具体的 Mapper 映射文件解析,每个 Mapper XML 被解析成一条MappedStatement对象(包含 SQL 语句、参数映射、结果映射等信息),存入Configuration.mappedStatements集合中。
  5. SqlSessionFactoryBuilder.build(Configuration)

    • 最后用解析好的Configuration对象创建DefaultSqlSessionFactory并返回。

第一阶段的目标很明确:Configuration对象是这个阶段的核心产物,其中包含了 MyBatis 运行所需的全部元数据信息。可以在XMLConfigBuilder.parseConfiguration()的不同方法中打断点,看看各个标签是如何被一步步解析并填充到Configuration对象中的。

第二阶段:获取代理对象和执行入口 —— 入口DefaultSqlSession.getMapper()

这一阶段是建立"数据库操作指挥部",当调用sqlSession.getMapper()时,它为背后的 Mapper 接口"凭空变出"一个可以执行 SQL 的实现类。

入口类DefaultSqlSession.getMapper(Class<T> type)

调用链(连贯地跟进去):

  1. DefaultSqlSession.getMapper()

    • 直接委托给configuration.getMapper(type, this)
  2. Configuration.getMapper()

    • 内部调用mapperRegistry.getMapper(type, sqlSession)
  3. MapperRegistry.getMapper()(核心 🔥,JDK 动态代理的创建点)

    • 先从knownMappers集合中根据接口类型找到对应的MapperProxyFactory
    • 然后调用mapperProxyFactory.newInstance(sqlSession)创建代理对象。
    • newInstance()方法内部:return (T) Proxy.newProxyInstance(... , new MapperProxy<>())
  4. MapperProxy类的构造与结构

    • MapperProxy是 JDK 动态代理的InvocationHandler实现类。
    • 当代理对象的方法被调用时,最终会执行MapperProxy.invoke()

在这里建议关注的点:看看MapperRegistry.knownMappers是什么时候被填充的——这正是在第一阶段解析 Mapper 映射关系时完成的。

第三阶段:SQL 执行与结果映射—— 真正的数据库操作链路

这是 MyBatis 源码中最长也最精彩的一条链路。当调用mapper.selectBusinessById(1)时,IDE 进入了MapperProxy.invoke()

执行链完整顺序(按顺序打断点跟进去):

步骤核心类/方法职责
1. 动态代理拦截MapperProxy.invoke()拦截 Mapper 接口的方法调用
2. 方法包装与路由MapperMethod.execute()根据 SQL 类型(SELECT/INSERT/UPDATE/DELETE)决定执行路径
3. SqlSession 入口DefaultSqlSession.selectOne()委托给 Executor 执行
4.缓存处理🔥CachingExecutor.query()检查二级缓存(跨 SqlSession 级别)
5.一级缓存🔥BaseExecutor.query()检查一级缓存(会话级别),若未命中则调用子类的doQuery()
6. 真正的查询逻辑SimpleExecutor.doQuery()创建 StatementHandler,准备 JDBC 执行环境
7.创建 StatementHandlerConfiguration.newStatementHandler()最终返回RoutingStatementHandler,它内部包装了真正的PreparedStatementHandler
8.参数绑定🔥PreparedStatementHandler.parameterize()内部调用DefaultParameterHandler.setParameters(),循环为PreparedStatement?占位符赋值
9. 执行 SQLPreparedStatementHandler.query()调用PreparedStatement.execute(),获得ResultSet
10.结果映射🔥DefaultResultSetHandler.handleResultSets()将 JDBCResultSet转换为 Java 对象(或集合)返回
11. 资源回收DefaultSqlSession.close()关闭数据库连接,释放资源

串联起来,完整调用链可以概括为一句话:SqlSession → MapperProxy → MappedStatement → Executor → StatementHandler → ParameterHandler → JDBC 执行 → ResultSetHandler → 返回结果

总结:快速定位的关键点

功能起点类核心方法
配置加载XMLConfigBuilderparseConfiguration()
代理生成MapperProxyFactorynewInstance()
方法拦截MapperProxyinvoke()
二级/一级缓存CachingExecutor/BaseExecutorquery()
SQL 参数绑定DefaultParameterHandlersetParameters()
JDBC 执行与结果映射PreparedStatementHandler/DefaultResultSetHandlerquery()handleResultSets()

通过这种方式,不需要一步一个脚印地找断点,而是跟着代码执行的顺序,自然地走进 MyBatis 的每一个核心环节

http://www.jsqmd.com/news/776551/

相关文章:

  • 专业开发者完全指南:高效配置八大网盘直链下载助手的最佳实践
  • 基于MCP协议构建AI工具调用服务器:从原理到实战
  • 蓝桥杯C/C++刷题避坑指南:从“疫情死亡率”到“得不到的爱情”,新手必知的5个思维陷阱
  • 长岛适合家庭入住民宿排行:五家口碑之选实测对比 - 奔跑123
  • 3分钟极速上手:碧蓝航线全自动脚本终极指南
  • FABulous嵌入式FPGA生成框架:从CSV定义到GDSII流片的完整指南
  • 30+文档平台一键下载:kill-doc免费文档下载工具完全指南
  • 深入Linux内核:从`/sys/devices/cpu/events/`文件看Intel PMU事件如何被抽象与管理
  • 告别SGM的漫长等待:用ELAS算法1秒搞定百万像素双目匹配(附C++/OpenCV实战代码)
  • 如何用ColabFold快速预测蛋白质结构:面向生物学研究者的完整指南
  • KMS_VL_ALL_AIO:一站式智能激活解决方案完全指南
  • 从零到点亮LED:基于STM8S105K4T6C的STVD+COSMIC项目创建全流程实录
  • OSS签名那些坑:从一次‘签名不匹配’报错,聊聊签名版本V1/V4和时钟漂移的实战影响
  • 告别命令行!用C语言封装AD9361 IIO驱动,在Vitis里实现一键读写(附完整代码)
  • Fast-GitHub终极指南:三步解决国内GitHub访问慢的完整方案
  • SoC验证范式变革:从工具堆砌到企业级数据驱动流程
  • 告别Windows依赖:在Ubuntu 22.04上搞定RK3568系统烧录(附rkflash.sh脚本详解)
  • 如何使用 PersistentVolumeClaim 动态挂载 NFS 存储卷
  • 别再死记硬背了!用“状态转换图”和“波形图”轻松吃透D触发器与JK触发器
  • 密钥管理体制PKI和KMI(二)
  • 洋葱路由原理与ConnectOnion实战:构建可控匿名通信网络
  • Windows 11 C盘爆红别慌!这5个隐藏的“空间杀手”软件,教你一键搬家到D盘
  • 用OpenCV和Python手把手实现Meanshift目标跟踪(附完整代码与避坑指南)
  • AI智能体安全实践:使用ActionBox为AI技能定义和执行行为契约
  • 2026年10款降AI率工具实测红黑榜:亲测有效!附免费降ai避坑教程 - 降AI实验室
  • 大白话科普:GAIA、AgentBench 到底是啥?
  • LCD1602自定义字符的5个高级玩法:从动态图标到简单动画
  • UseZombie:构建安全可控的AI智能体生产级运行平台
  • 福州GEO优化系统TOP10深度测评:主流方案对比与选型指南(2026年) - 博客湾
  • 别再手动调Excel格式了!用EasyExcel 3.x模板填充,5分钟搞定复杂报表导出(附完整代码)