MyBatis源码与性能优化深度解析:把ORM框架核心讲透,吊打面试官

张开发
2026/4/18 19:48:35 15 分钟阅读

分享文章

MyBatis源码与性能优化深度解析:把ORM框架核心讲透,吊打面试官
MyBatis源码与性能优化深度解析:把ORM框架核心讲透,吊打面试官🎯写在前面:MyBatis是Java ORM框架的经典之作。相比JPA,MyBatis更加灵活可控;相比JDBC,MyBatis又屏蔽了大量样板代码。但你真的了解MyBatis的底层原理吗?这篇文章,将带你从源码层面深度剖析MyBatis!一、MyBatis核心架构:一次请求的完整流程1.1 整体架构图┌─────────────────────────────────────────────────────────────────────┐ │ MyBatis整体架构 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ API层(SqlSession) │ │ │ │ SqlSessionFactory → SqlSession → Mapper Proxy │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ 核心处理层 │ │ │ │ Executor → StatementHandler → ParameterHandler → ResultSetHandler │ │ └────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ 基础支撑层 │ │ │ │ TransactionManager | ConnectionPool | Cache(一级/二级) │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘1.2 一次查询的完整流程// 1. 获取SqlSession(门面入口)SqlSessionsqlSession=sqlSessionFactory.openSession();// 2. 获取Mapper代理对象UserMapperuserMapper=sqlSession.getMapper(UserMapper.class);// 3. 调用接口方法Useruser=userMapper.findById(1L);// 4. 底层原理:jdk动态代理// MyBatis使用MapperProxyFactory为每个Mapper接口创建代理对象// 代理对象的invoke方法执行以下逻辑:// ① 从Configuration中获取MappedStatement// ② 调用Executor执行SQL// ③ 处理参数// ④ 处理结果集1.3 核心组件详解/** * 核心组件及其职责 */publicclassMyBatisCoreComponents{// 1. SqlSessionFactoryBuilder:构建SqlSessionFactory// 负责解析mybatis-config.xml和mapper.xml,生成Configuration// 2. SqlSessionFactory:SqlSession工厂// 负责创建SqlSession实例// 3. SqlSession:MyBatis的门面接口// 提供了所有操作数据库的方法:selectOne, selectList, insert, update, delete// 4. Executor:执行器(核心)// - SimpleExecutor:简单执行器// - ReuseExecutor:复用执行器(缓存Statement)// - BatchExecutor:批量执行器(批量操作)// - CachingExecutor:缓存执行器(装饰器模式,包装以上三种)// 5. StatementHandler:语句处理器// - PreparedStatementHandler:预编译SQL(防止SQL注入)// - SimpleStatementHandler:简单SQL// - CallableStatementHandler:存储过程// 6. ParameterHandler:参数处理器// 将Java对象转换为SQL参数// 7. ResultSetHandler:结果集处理器// 将ResultSet转换为Java对象// 8. TypeHandler:类型转换器// JDBC类型 ↔ Java类型 之间的转换}二、SQL执行原理:Executor深度剖析2.1 Executor家族┌─────────────────────────────────────────────────────────────────────┐ │ Executor执行器体系 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ Executor(接口) │ │ ↑ │ │ ┌───────────┴───────────┐ │ │ │ │ │ │ BaseExecutor CachingExecutor │ │ │ │ │ │ ┌─────────┼─────────┐ ┌──────┴──────┐ │ │ ↓ ↓ ↓ ↓ ↓ │ │ Simple Reuse Batch ←── 包装 ──→ 其他执行器 │ │ Executor Executor Executor │ │ │ │ 职责: │ │ - 管理Statement生命周期 │ │ - 一级缓存管理(BaseExecutor) │ │ - 事务管理 │ │ │ └─────────────────────────────────────────────────────────────────────┘2.2 执行流程源码解析// 核心流程:SqlSession.selectList()publicclassSqlSessionselectList(Stringstatement,Objectparameter){// 1. 从Configuration获取MappedStatementMappedStatementms=configuration.getMappedStatement(statement);// 2. 调用Executor执行returnexecutor.query(ms,parameter,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);}// BaseExecutor.query()publicclassEListEquery(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler){// 1. 创建BoundSql(包含最终SQL和参数)BoundSqlboundSql=ms.getBoundSql(parameter);// 2. 生成缓存Key(一级缓存的key)CacheKeykey=createCacheKey(ms,parameter,rowBounds,boundSql);// 3. 查询(一级缓存)ListElist=queryFromDatabase(ms,parameter,rowBounds,resultHandler,key,boundSql);returnlist;}privateEListEqueryFromDatabase(...){// 1. 先查询一级缓存ListElist=localCache.getObject(key);if(list!=null){returnlist;// 缓存命中}// 2. 缓存未命中,查询数据库list=doQuery(ms,parameter,rowBounds,resultHandler,boundSql);// 3. 放入一级缓存localCache.putObject(key,list);returnlist;}// CachingExecutor.query()(二级缓存)publicEListEquery(MappedStatementms,Objectparameter,...){// 1. 查询二级缓存Cachecache=ms.getCache();if(cache!=null){flushCacheIfRequired(ms);// 根据flushCache配置决定是否清空CacheKeykey=createCacheKey(ms,parameter,rowBounds,boundSql);ListElist=cache.getObject(key);if(list!=null){returnlist;// 二级缓存命中}}// 2. 调用被包装的执行器查询ListElist=delegate.query(ms,parameter,rowBounds,resultHandler,boundSql);// 3. 放入二级缓存if(cache!=null){cache.putObject(key,list);}returnlist;}三、缓存机制:二级缓存避坑指南3.1 一级缓存 vs 二级缓存┌─────────────────────────────────────────────────────────────────────┐ │ MyBatis缓存机制 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ 一级缓存(SqlSession级) │ │ │ │ │ │ │ │ 作用域:SqlSession │ │ │ │ 存储介质: PerpetualCache(HashMap) │ │ │ │ 生命周期: SqlSession关闭后清空 │ │ │ │ 默认开启: 是 │ │ │ │ 所属组件: BaseExecutor │ │ │ │ │ │ │ │ 缓存失效场景: │ │ │ │ ❌ 不同的SqlSession │ │ │ │ ❌ 查询条件不同 │ │ │ │ ❌ 执行了增删改操作(会清空该SqlSession的缓存) │ │ │ │ ❌ 手动调用clearCache() │ │ │ │ ❌ 事务回滚 │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ 二级缓存(Mapper级) │ │ │ │ │ │ │ │ 作用域:Mapper级别,整个应用共享 │ │ │ │ 存储介质:可配置(PerpetualCache、Ehcache、Redis等) │ │ │ │ 生命周期: 应用运行期间 │ │ │ │ 默认开启: 否(需要配置cache) │ │ │ │ 所属组件: CachingExecutor │ │ │ │ │ │ │ │ 特点: │ │ │ │ ✅ 以Mapper为namespace,隔离不同Mapper的缓存 │ │ │ │ ✅ 事务提交后才写入(需要SqlSession提交/close) │ │ │ │ ⚠️ 与一级缓存配合使用 │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘3.2 二级缓存配置与使用!-- mapper.xml中开启二级缓存 --mappernamespace="com.example.mapper.UserMapper"!-- eviction: 淘汰策略 - LRU: 最近最少使用(默认) - FIFO: 先进先出 - SOFT: 软引用 - WEAK: 弱引用 flushInterval: 刷新间隔(毫秒),不设置则不刷新 readOnly: 是否只读 size: 缓存对象数量 --cacheeviction="LRU"flushInterval="60000"readOnly="false"size="512"/!-- 查询结果放入二级缓存 --selectid="findById"resultType="User"useCache="true"SELECT * FROM user WHERE id = #{id}

更多文章