前言很多人面试被问 MyBatis 工作原理张口就来 “ORM 框架写 XML 操作数据库”这种回答最多算及格远达不到大厂的要求。面试官真正想听的不是你会不会用配置而是你有没有理解框架的设计思想 —— 为什么互联网公司清一色用 MyBatis而不用 Hibernate核心就两个字控制权。当你能把一个接口从 3 秒优化到 300 毫秒时你就会明白这种对 SQL 的精准控制权有多重要。这篇文章就从动态代理层、核心执行层、设计思想层三层深度拆解 MyBatis 原理再把一级二级缓存的设计、坑点、大厂最佳实践讲透最后给你一套可直接套用的面试高分回答模板。第一层Mapper 接口为什么没有实现类—— 动态代理机制我们只写 Mapper 接口不用写实现类调用方法就能执行 SQL底层的核心就是JDK 动态代理。1.1 代理生成逻辑MyBatis 在启动时会为每个 Mapper 接口生成一个动态代理对象MapperProxy。你调用接口方法时实际调用的是代理对象的invoke()方法它会根据方法签名去全局配置Configuration里找到对应的MappedStatement。1.2 关键概念MappedStatement每条 SQL 在 MyBatis 启动时都会被解析封装成一个MappedStatement对象里面包含SQL 模板参数映射规则结果映射规则缓存配置所有和这条 SQL 相关的信息全部预加载完成不是每次执行都解析 XML这就是为什么 MyBatis 启动相对慢但运行时速度快的原因。1.3 极简代码示例// 你只需要写接口 public interface UserMapper { User selectById(Long id); } // 底层自动生成代理对象 UserMapper mapper sqlSession.getMapper(UserMapper.class); // 调用方法实际走的是代理的invoke方法 User user mapper.selectById(1L);第二层核心执行机制 —— 一次查询完整五步流程调用 Mapper 方法时内部完整的执行流程可以拆解为五步定位 StatementSqlSession根据方法对应的 Statement ID找到预加载的MappedStatement参数解析根据配置规则把入参解析成可执行的 SQLif、foreach等动态 SQL 标签就在这一步处理获取连接Executor执行器从数据库连接池获取连接执行 SQLStatementHandler把参数绑定到PreparedStatement上执行 SQL 拿到ResultSet结果映射ResultSetHandler把结果集按映射规则转换成 Java 对象返回2.1 Executor 执行器体系执行器是 MyBatis 的核心调度组件一共三种基础执行器 一种缓存装饰器性能差异非常大执行器类型核心特点适用场景SimpleExecutor每次执行完就关闭 Statement默认配置普通场景简单通用ReuseExecutor复用 Statement减少预编译开销高频查询场景性能更优BatchExecutor支持批量操作批量提交批量插入、批量更新场景CachingExecutor装饰器模式包裹基础执行器负责二级缓存读写开启二级缓存时自动启用核心设计巧思缓存不是夹在业务代码层而是夹在执行器层。CachingExecutor 只负责缓存的查询和写入真正的数据库查询还是交给内部的基础执行器去做职责分离非常清晰。第三层设计思想 —— 半自动 ORM 的本质是控制权这才是面试官真正想听的深度也是 MyBatis 能碾压 Hibernate 成为互联网标配的根本原因。3.1 全自动 ORM 的痛点往前十年大家都在用 Hibernate 这种全自动 ORM 框架自动生成 SQL听起来很省心但真实业务里 80% 的性能问题都出在 SQL 上表关联复杂时框架自动生成的 SQL 可能关联 5 张表走全表扫描十万行数据3 秒才能返回想优化 SQL、加索引提示、调整连接顺序都非常困难最后往往得跳出 ORM 写原生 SQL反而失去了便利性3.2 MyBatis 的设计哲学MyBatis 走的是半自动 ORM路线它不帮你生成 SQL只负责把你写的 SQL 和 Java 代码连接起来。 你想怎么写 SQL 就怎么写想加索引提示就加想怎么优化就怎么优化这种对 SQL 的精准控制权才是性能优化的根本。真实案例一个订单查询接口复杂关联下 Hibernate 自动生成的 SQL 要 3 秒返回手写优化 SQL只关联两张表用inner join配合索引扫描一百行300 毫秒就能返回。十倍的性能差距就是 SQL 控制权的价值。深度拓展两级缓存设计与权衡MyBatis 的缓存设计最能体现框架的成熟度 —— 它不替你做决定而是把选择权交给你要性能还是要强一致性自己选。4.1 一级缓存SqlSession 级作用域同一个 SqlSession 内默认开启生命周期和事务基本一致正常情况下不会出现跨事务脏读底层原理本地内存缓存key 由 statementId、SQL、参数共同组成经典坑点同一个 SqlSession 里如果数据被外部程序修改而没触发 MyBatis 的更新操作就会读到旧数据。Spring 事务中 SqlSession 会被复用分布式场景下更容易踩坑。大厂最佳实践设置localCacheScopeSTATEMENT让一级缓存只在单条语句内生效从根源上规避事务级缓存带来的脏读风险。4.2 二级缓存Mapper 级作用域跨 SqlSession 共享同一个 Mapper namespace 下共用需要手动开启设计特点插件化设计默认只有内存实现可以自定义实现一致性问题分布式场景下一个节点更新了数据库其他节点还挂着旧缓存就会读到脏数据大厂最佳实践不使用默认的内存二级缓存而是实现 MyBatis 的Cache接口把缓存放到 Redis或者直接在业务层做缓存既能享受性能提升又能解决分布式一致性问题。面试高分回答模板面试官问 “MyBatis 工作原理是什么”按这个逻辑答直接和普通候选人拉开差距我会从设计目标、核心机制、设计权衡三个层面来说 第一设计目标MyBatis 是半自动 ORM 框架核心设计是 SQL 和 Java 代码分离保留开发者对 SQL 的绝对控制权方便复杂场景下的性能优化。 第二核心机制接口层通过 JDK 动态代理为 Mapper 接口生成代理对象启动时解析所有 SQL 封装成 MappedStatement 预加载执行层通过 SqlSession 调度 Executor经过参数解析、SQL 绑定、执行、结果映射五步完成查询缓存采用装饰器模式实现两级缓存一级缓存是 SqlSession 级二级缓存是 Mapper 级 第三设计权衡不做全自动 SQL 生成是为了保留 SQL 控制权方便复杂查询的性能优化分两级缓存是为了权衡性能和数据一致性开发者可以根据场景选择实际生产中大厂通常会把一级缓存设为语句级规避脏读二级缓存用 Redis 替代解决分布式一致性问题这样回答面试官一听就知道你是真懂而不是只会背配置。思维发散延伸与进阶5.1 常见坑点排查一级缓存脏读分布式、多事务场景下优先调整localCacheScope二级缓存失效增删改操作会清空对应 namespace 的二级缓存频繁写的场景不适合开启分页插件与缓存冲突分页插件会改写 SQL可能导致缓存 key 不匹配5.2 性能优化方向高频查询场景启用ReuseExecutor复用 Statement 减少预编译开销批量操作使用BatchExecutor大幅提升写入性能读多写少场景合理使用缓存写多读少场景直接关闭二级缓存针对慢 SQL直接在 XML 里优化 SQL 语句这正是 MyBatis 的核心优势5.3 主流 ORM 框架横向对比框架类型SQL 控制权性能上限开发效率适用场景MyBatis半自动 ORM完全可控高可极致优化中等需要写 SQL互联网项目、复杂查询、性能要求高Hibernate全自动 ORM弱自动生成低复杂查询难优化高不用写 SQL传统企业项目、简单 CRUDSpring Data JPA全自动 ORM一般可自定义中等高微服务项目、简单业务MyBatis-Plus半自动增强可控自带 CRUD高很高单表不用写 SQL国内互联网项目兼顾效率和性能