一碗牛肉面引发的"灵魂拷问"
故事要从上周三讲起。
我背着电脑包,走进了一家中厂的面试间。面试官看起来很友好,一边喝着豆浆一边刷简历,看上去并没有那么"凶"。
前半场聊得风平浪静,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。"
你可以理解为:
- 每一个 、、、 标签,都会被解析成一个 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,需配置 或开启全局。 一级缓存存在于 BaseExecutor 的 localCache 中,是基于 Statement 的缓存。 二级缓存则是通过装饰器模式,在 CachingExecutor 中实现的,如果一个Mapper开启了 ,它就会参与缓存机制。 我还举了一个例子说明什么时候会失效,比如: 最后的总结是灵魂所在! 最后我这样收尾: "MyBatis的底层原理虽然结构清晰,但也细节繁多。想真正掌握它,建议从源码看起,比如从 SqlSessionFactoryBuilder → XMLConfigBuilder → MapperRegistry → Executor 这一条主线开始阅读,理解它是如何'工厂 → 注册器 → 执行器'联动的。" 我还补了一句: "在生产中,我们还需要配合 PageHelper、MyBatis-Plus 等框架,注意性能调优、懒加载、缓存穿透等问题。" 这波总结之后,面试官笑了笑: "小米,这块答得可以。" 嘿,差点翻车的MyBatis,最后成了加分项! 附件彩蛋:我常用的MyBatis调试技巧 面试之余,我还和面试官"客套"地聊了几句技术: 用日志打印SQL: 配置 logImpl=STDOUT_LOGGING 看SQL原始输出。 打开动态SQL打印: 注意拼接过程,看 Mapper XML 中的 、。 手动执行 Mapper XML: 用工具如MyBatisX 插件直接在IDE里调试 SQL。 小米的复盘思维 其实面试被问到 MyBatis 原理并不难,难的是你能不能讲清楚一个完整流程,最好能结合源码主线和开发实践,一步一步带着对方进入你的节奏。 记住一句话:讲技术,不是背诵API,而是讲出"数据是如何流动"的。 结语:别怕深原理,多拆多练多讲 MyBatis 是我们日常开发最常用的 ORM 框架之一,想进中大厂,光会用还不够,要能讲出底层设计思路。 下次你再面试,不妨也试着用我的节奏,把自己熟悉的部分讲得透一点、慢一点、准一点。 END 如果你觉得这篇文章对你有帮助,欢迎点赞、在看、转发! 我们下次面经见! 我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!