一碗牛肉面引发的“灵魂拷问”

故事要从上周三讲起。

我背着电脑包,走进了一家中厂的面试间。面试官看起来很友好,一边喝着豆浆一边刷简历,看上去并没有那么“凶”。

前半场聊得风平浪静,Spring、Redis、消息队列、线程池,全是我的舒适区,我都快以为稳了。

直到他放下了手里的豆浆,眼神突然认真了几分。

“小米,我看你简历里写了几年MyBatis经验,那你能说一下MyBatis的解析过程和运行原理吗?”

好家伙,来了来了!

我脑袋里的MyBatis这锅“面”,突然开始腾腾冒气,关键时刻,得上干货!

先别慌!我们拆成几个步骤来看

为了让面试官听懂,我选择用“从源码角度,但不绕进细节”的方式回答,结构清晰、逻辑递进。下面是我答题的思路,也分享给你们:

第一步:MyBatis的大致执行流程

我开口就先画了个大轮廓,给面试官一个直观的印象:

“MyBatis的大致执行流程可以概括为:加载配置 → 解析Mapper → 创建SqlSession → 执行SQL语句 → 处理结果集。

讲的时候,我在白板上画了下面这张“心智图”:

只会写 Mapper 就想进大厂?MyBatis 原理你真懂了吗?_XML

这张图是核心架构的“高速公路图”,接下来我开始一点点往里面“铺砖”。

第二步:从配置文件说起(mybatis-config.xml)

我说,其实一切的起点就是那个熟悉的 mybatis-config.xml。

MyBatis 启动时会通过 SqlSessionFactoryBuilder 解析这个 XML,读取里面的环境配置(如数据源、事务、插件)和映射配置(Mappers)。

背后干活的是 XMLConfigBuilder,它会将配置加载进 Configuration 对象,这是 MyBatis 的“全局大脑”。

只会写 Mapper 就想进大厂?MyBatis 原理你真懂了吗?_XML_02

注意:build() 里其实就触发了整个解析流程。

第三步:Mapper 的解析流程

面试官听到这开始点头,我继续加码。

“Mapper 其实是最核心的部分,它既有接口,又有 XML 配置。MyBatis 会先将 XML 映射文件解析成一个个 MappedStatement。”

你可以理解为:

  • 每一个 <select>、<insert>、<update>、<delete> 标签,都会被解析成一个 MappedStatement 对象,注册到 Configuration 中的 mappedStatements Map 里。

这个 Map 是一个超级关键的存储器!

只会写 Mapper 就想进大厂?MyBatis 原理你真懂了吗?_SQL_03

“而这个解析的过程,是由 XMLMapperBuilder 完成的,它会读取每一个 Mapper XML,并把它注册到全局配置里。”

我举了一个例子:

只会写 Mapper 就想进大厂?MyBatis 原理你真懂了吗?_XML_04

这个最终会变成一个 id 为 namespace.id 的 MappedStatement,存在全局容器里。

第四步:运行时的 SqlSession 和执行流程

到这,面试官已经坐直了。

我接着说:

“当我们在代码里执行一条查询,比如 userMapper.findUserById(1),其实底层发生了很多事。”

我用这段代码举例:

只会写 Mapper 就想进大厂?MyBatis 原理你真懂了吗?_XML_05

这三行代码的运行流程,其实是这样:

  • openSession():创建一个 DefaultSqlSession,关联 Executor(执行器)。
  • getMapper():创建 Mapper 代理对象(MapperProxy),其实是动态代理。
  • findUserById():调用代理方法,最终由 MapperMethod 调用到 Executor。
  • Executor.query():查询流程走到 CachingExecutor → BaseExecutor → StatementHandler → PreparedStatement。
  • SQL 执行:底层通过 JDBC 发送 SQL。
  • 返回结果:通过 ResultSetHandler 映射结果,封装成返回对象。

第五步:MyBatis 核心对象解析

我知道这个问题的难点在于——你得知道那些类是干嘛的,但又不能讲太啰嗦。

所以我专门准备了一套“五大核心对象口诀”:

配执映参结,五大核心,一串到底!

  • Configuration:大脑,所有信息的注册中心。
  • Executor:执行器,真正执行SQL的主力。
  • MappedStatement:映射语句,封装了每个SQL和参数信息。
  • ParameterHandler:处理参数的工具类。
  • ResultSetHandler:结果映射器,处理ResultSet返回对象。

第六步:一级缓存 & 二级缓存 简述

面试官补了一刀:“那你讲讲MyBatis缓存吧。”

我说:缓存分两级:

  • 一级缓存(Session级别):SqlSession 生命周期内有效,默认开启。
  • 二级缓存(Mapper级别):跨Session,需配置 <cache/> 或开启全局。

一级缓存存在于 BaseExecutor 的 localCache 中,是基于 Statement 的缓存。

二级缓存则是通过装饰器模式,在 CachingExecutor 中实现的,如果一个Mapper开启了 <cache>,它就会参与缓存机制。

我还举了一个例子说明什么时候会失效,比如:

只会写 Mapper 就想进大厂?MyBatis 原理你真懂了吗?_缓存_06

最后的总结是灵魂所在!

最后我这样收尾:

“MyBatis的底层原理虽然结构清晰,但也细节繁多。想真正掌握它,建议从源码看起,比如从 SqlSessionFactoryBuilder → XMLConfigBuilder → MapperRegistry → Executor 这一条主线开始阅读,理解它是如何‘工厂 → 注册器 → 执行器’联动的。”

我还补了一句:

“在生产中,我们还需要配合 PageHelper、MyBatis-Plus 等框架,注意性能调优、懒加载、缓存穿透等问题。”

这波总结之后,面试官笑了笑:

“小米,这块答得可以。”

嘿,差点翻车的MyBatis,最后成了加分项!

附件彩蛋:我常用的MyBatis调试技巧

面试之余,我还和面试官“客套”地聊了几句技术:

  • 用日志打印SQL:配置 logImpl=STDOUT_LOGGING 看SQL原始输出。
  • 打开动态SQL打印:注意拼接过程,看 Mapper XML 中的 <if>、<foreach>。
  • 手动执行 Mapper XML:用工具如MyBatisX 插件直接在IDE里调试 SQL。

小米的复盘思维

其实面试被问到 MyBatis 原理并不难,难的是你能不能讲清楚一个完整流程,最好能结合源码主线和开发实践,一步一步带着对方进入你的节奏。

记住一句话:讲技术,不是背诵API,而是讲出“数据是如何流动”的。

结语:别怕深原理,多拆多练多讲

MyBatis 是我们日常开发最常用的 ORM 框架之一,想进中大厂,光会用还不够,要能讲出底层设计思路。

下次你再面试,不妨也试着用我的节奏,把自己熟悉的部分讲得透一点、慢一点、准一点。

END

如果你觉得这篇文章对你有帮助,欢迎点赞、在看、转发!

我们下次面经见!

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!