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

一碗牛肉面引发的“灵魂拷问”
故事要从上周三讲起。

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

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

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

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

好家伙,来了来了!

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

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

第一步:MyBatis的大致执行流程
我开口就先画了个大轮廓,给面试官一个直观的印象:

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

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

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

第二步:从配置文件说起(mybatis-config.xml)
我说,其实一切的起点就是那个熟悉的 mybatis-config.xml。

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

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

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

第三步:Mapper 的解析流程
面试官听到这开始点头,我继续加码。

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

你可以理解为:

每一个 <select>、<insert>、<update>、<delete> 标签,都会被解析成一个 MappedStatement 对象,注册到 Configuration 中的 mappedStatements Map 里。
这个 Map 是一个超级关键的存储器!

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

我举了一个例子:

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

第四步:运行时的 SqlSession 和执行流程
到这,面试官已经坐直了。

我接着说:

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

我用这段代码举例:

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

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>,它就会参与缓存机制。

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

最后的总结是灵魂所在!
最后我这样收尾:

“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
如果你觉得这篇文章对你有帮助,欢迎点赞、在看、转发!

我们下次面经见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值