MyBatis 架构与核心组件全解析:从三层架构到 SQL 执行链路
MyBatis 的架构设计清晰且模块化,通过 基础支持层、核心处理层、接口层 三层结构实现 “配置驱动、插件可扩展、低侵入” 的特性。本文深入拆解每一层的核心组件、职责及协作关系,并结合 SQL 执行全链路,帮你理解 MyBatis 如何将 “配置” 转化为 “数据库操作”,构建完整的技术认知。
MyBatis 三层架构总览
MyBatis 的三层架构并非物理隔离,而是按 “职责划分” 的逻辑分层,各层组件协同工作,形成从 “接口调用” 到 “数据库响应” 的闭环。三层的核心职责与组件对应关系如下:
架构分层 | 核心职责 | 关键组件 |
---|---|---|
接口层 | 提供对外 API,简化开发者调用 | SqlSession 、MapperProxy (动态代理) |
核心处理层 | 实现 SQL 执行与结果映射的核心逻辑 | Executor (执行器)、StatementHandler (SQL 处理器)、ParameterHandler (参数处理器)、ResultSetHandler (结果处理器)、Configuration (配置中心) |
基础支持层 | 为核心层提供通用支撑能力,保证框架稳定性 | 数据源、事务管理、日志、类型转换(TypeHandler )、缓存、XML/OGNL 解析器 |
分层详解:组件职责与协作
1. 基础支持层:框架的 “地基”
基础支持层是 MyBatis 的 “工具集”,为核心处理层提供通用能力,不直接参与 SQL 执行,但决定了框架的扩展性、稳定性和性能。
(1)核心组件与职责
组件 | 核心职责 | 关键实现 / 配置 |
---|---|---|
数据源(DataSource) | 管理数据库连接,优化连接复用 | - 内置实现:UnpooledDataSource (无池)、PooledDataSource (有池); - 第三方集成:HikariCP (Spring Boot 默认)、Druid (阿里) |
事务管理(Transaction) | 管理事务生命周期(提交 / 回滚 / 关闭) | - 内置类型:JdbcTransaction (依赖 JDBC 事务)、ManagedTransaction (交给容器管理); - 由 TransactionFactory 工厂创建 |
日志(Logging) | 打印 SQL 执行日志,便于调试 | 适配主流日志框架:SLF4J、Log4j2、Commons Logging、JDK Logging;通过 logImpl 配置 |
类型转换(TypeHandler) | 实现 Java 类型 ↔ JDBC 类型的双向转换 | - 内置类型:StringTypeHandler 、IntegerTypeHandler 等(覆盖常用类型); - 自定义扩展:实现 TypeHandler 接口(如 LocalDateTimeTypeHandler ) |
缓存(Cache) | 提供二级缓存支持,减少数据库访问 | - 内置实现:PerpetualCache (内存缓存)、LruCache (LRU 回收); - 第三方集成:Redis、EHCache(实现 Cache 接口) |
解析器(Parser) | 解析 XML 配置、OGNL 表达式、SQL 语句 | - XMLConfigBuilder (全局配置解析)、XMLMapperBuilder (Mapper 解析); - XPathParser (XML 节点解析)、OGNLExpressionEvaluator (OGNL 表达式计算) |
反射工具(Reflector) | 优化 Java 反射性能,简化属性访问 | ReflectorFactory 生成 Reflector 对象,缓存类的属性、方法信息,避免重复反射 |
(2)核心作用
- 解耦通用能力:将数据源、事务、日志等通用功能抽象为独立组件,核心层无需关注细节(如连接池实现);
- 扩展灵活性:支持第三方组件替换(如用 Druid 替换内置数据源),通过配置即可生效,无需修改核心代码。
2. 核心处理层:SQL 执行的 “引擎”
核心处理层是 MyBatis 的 “大脑”,负责将 “配置信息” 转化为 “实际 SQL 执行”,并完成结果映射。这一层的组件是 MyBatis 最核心的逻辑载体。
(1)核心组件与职责(按 SQL 执行流程排序)
① Configuration
:全局配置中心
- 职责:存储所有配置信息(全局设置、数据源、Mapper 映射、插件等),是 MyBatis 初始化的最终产物,贯穿整个框架生命周期;
- 核心内容:
mappedStatements
:存储所有MappedStatement
(封装单条 SQL 的元数据,如 SQL 语句、参数类型、结果类型);resultMaps
:存储所有ResultMap
(结果映射规则,解决字段与属性名不一致);environment
:存储数据库环境(数据源 + 事务管理器);plugins
:存储注册的插件(拦截器链)。
- 地位:所有核心组件(
Executor
、StatementHandler
等)的创建都依赖Configuration
,是组件协作的 “数据中枢”。
② Executor
:执行器(SQL 调度核心)
职责:MyBatis 的 “执行调度中心”,负责协调
StatementHandler
、ParameterHandler
、ResultSetHandler
完成 SQL 执行,同时管理一级缓存和事务;核心类型(根据execType配置):
| 执行器类型 | 特点 | 适用场景 |
| ————————- | —————————————————————- | ——————————————— |
|SimpleExecutor
| 默认实现,每次执行 SQL 都创建新Statement
| 单条 SQL 执行(大多数场景) |
|ReuseExecutor
| 复用Statement
(按 SQL 缓存) | 重复执行相同 SQL(如循环查询) |
|BatchExecutor
| 批量执行 SQL(仅支持 INSERT/UPDATE/DELETE) | 批量操作(如批量插入) |
|CachingExecutor
| 装饰器模式,添加二级缓存支持 | 开启二级缓存时自动生效 |核心流程:
- 接收
SqlSession
的请求(如selectOne
); - 从
Configuration
中获取MappedStatement
; - 创建
StatementHandler
并委托其执行 SQL; - 管理一级缓存(查询前查缓存,查询后写缓存)。
- 接收
③ StatementHandler
:SQL 执行处理器(与数据库交互)
职责:直接与数据库交互,负责创建
Statement
(JDBC 对象)、预编译 SQL、执行 SQL 并返回ResultSet
,是 “承上启下” 的关键组件;核心类型(根据statementType配置):
| 处理器类型 | 对应 JDBC 对象 | 特点 |
| ————————————— | —————————- | —————————————————— |
|SimpleStatementHandler
|Statement
| 无参数 SQL(不支持?
占位符) |
|PreparedStatementHandler
|PreparedStatement
| 预编译 SQL(支持参数占位符,防注入) |
|CallableStatementHandler
|CallableStatement
| 调用存储过程(支持IN/OUT
参数) |核心流程:
- 从
Executor
接收MappedStatement
和参数; - 调用
Connection.prepareStatement()
预编译 SQL; - 委托
ParameterHandler
设置 SQL 参数; - 执行 SQL(
executeQuery()
/executeUpdate()
),返回ResultSet
; - 委托
ResultSetHandler
处理结果。
- 从
④ ParameterHandler
:参数处理器
- 职责:将 Mapper 方法的参数(如
user.getId()
)映射到预编译 SQL 的?
占位符,解决 “Java 参数 → JDBC 参数” 的类型转换; - 核心逻辑:
- 从
BoundSql
中获取参数映射列表(parameterMappings
,记录参数名、Java 类型、JDBC 类型); - 通过
TypeHandler
将 Java 参数值转换为 JDBC 类型(如LocalDateTime
→Timestamp
); - 调用
PreparedStatement.setXxx()
方法设置参数(如setInt()
、setString()
)。
- 从
- 注意:MyBatis 内置的
DefaultParameterHandler
已覆盖 99% 的场景,无需自定义,除非有特殊类型转换需求。
⑤ ResultSetHandler
:结果集处理器
- 职责:将 JDBC 的
ResultSet
(数据库返回结果)解析为 Java 实体类(或 List/Map),解决 “JDBC 结果 → Java 对象” 的映射; - 核心逻辑:
- 从
MappedStatement
中获取ResultMap
(结果映射规则); - 遍历
ResultSet
的每一行数据; - 根据
ResultMap
匹配 “数据库列名 → 实体属性名”,通过TypeHandler
转换类型(如Timestamp
→LocalDateTime
); - 调用实体类的
setter
方法设置属性,或通过构造器注入(constructor
映射); - 处理关联查询(
association
/collection
):触发嵌套查询或解析嵌套结果。
- 从
- 关键能力:支持自动映射(
autoMapping
)、驼峰命名映射(mapUnderscoreToCamelCase
)、复杂关联映射,是 MyBatis 结果处理的核心。
⑥ Plugin
:插件(拦截器)
- 职责:基于代理模式,拦截核心组件的方法(如
Executor.query()
、StatementHandler.prepare()
),实现功能增强(如分页、日志、性能监控); - 核心原理:
- 插件实现
Interceptor
接口,通过@Signature
注解指定拦截的组件和方法; - MyBatis 初始化时,将插件注册到
Configuration.plugins
; - 创建核心组件(如
Executor
)时,通过Plugin.wrap()
生成代理对象; - 调用组件方法时,触发插件的
intercept()
方法,执行增强逻辑。
- 插件实现
- 常见应用:PageHelper 分页插件(拦截
Executor.query()
改写 SQL)、日志插件(拦截StatementHandler
打印 SQL)。
3. 接口层:开发者的 “入口”
接口层是 MyBatis 对外暴露的 API,旨在简化开发者调用,屏蔽底层复杂逻辑(如 Executor
、StatementHandler
的协作)。
(1)核心组件与职责
① SqlSession
:数据库会话
- 职责:相当于 JDBC 的
Connection
,代表一次数据库会话,提供 CRUD 方法(selectOne
、insert
、update
、delete
),是开发者直接操作的入口; - 核心实现:
DefaultSqlSession
(默认实现),持有Configuration
和Executor
,将方法调用委托给Executor
; - 生命周期:单次业务请求(线程不安全,需及时关闭,避免连接泄漏)。
② MapperProxy
:Mapper 接口动态代理
- 职责:MyBatis 不要求开发者实现 Mapper 接口,而是通过 JDK 动态代理生成
MapperProxy
对象,将 Mapper 方法调用转发给SqlSession
; - 核心逻辑:
- 调用 Mapper 方法(如
userMapper.selectById(1)
)时,触发MapperProxy.invoke()
; - 解析方法信息(如
namespace + methodName
),找到对应的MappedStatement
; - 调用
SqlSession
的对应方法(如sqlSession.selectOne()
),完成 SQL 执行;
- 调用 Mapper 方法(如
- 优势:无侵入式开发,开发者只需定义 Mapper 接口和 XML,无需编写实现类。
SQL 执行全链路:三层组件协作实战
以 “调用 userMapper.selectById(1)
查询用户” 为例,完整拆解三层组件的协作流程,理解 MyBatis 如何将 “接口调用” 转化为 “数据库响应”:
1. 链路总览
2. 关键步骤拆解(结合组件职责)
步骤 1:接口调用触发动态代理
- 开发者注入
UserMapper
接口,实际注入的是MapperProxy
代理对象; - 调用
userMapper.selectById(1)
时,触发MapperProxy.invoke()
方法。
步骤 2:解析方法并委托 SqlSession
MapperProxy
解析方法的namespace
(com.xxx.UserMapper
)和methodName
(selectById
),拼接为MappedStatement
的id
(com.xxx.UserMapper.selectById
);- 调用
sqlSession.selectOne("com.xxx.UserMapper.selectById", 1)
,将请求委托给SqlSession
。
步骤 3:Executor 调度核心组件
SqlSession
(DefaultSqlSession
)调用executor.query(ms, parameter, rowBounds, resultHandler)
;Executor
(如CachingExecutor
)先查询二级缓存,未命中则调用SimpleExecutor
的query()
;SimpleExecutor
创建StatementHandler
(PreparedStatementHandler
),并传入MappedStatement
和参数。
步骤 4:StatementHandler 与数据库交互
StatementHandler.prepare(connection)
:调用Connection.prepareStatement("SELECT * FROM user WHERE id = ?")
,预编译 SQL;StatementHandler.parameterize(statement)
:委托ParameterHandler
调用preparedStatement.setInt(1, 1)
,设置参数;StatementHandler.query(statement, resultHandler)
:执行preparedStatement.executeQuery()
,获取ResultSet
。
步骤 5:ResultSetHandler 封装结果
ResultSetHandler.handleResultSets(statement)
:遍历ResultSet
,根据ResultMap
规则(如id
→id
,user_name
→userName
),通过TypeHandler
转换类型;- 创建
User
对象,调用setter
方法设置属性,返回User
列表。
步骤 6:缓存与结果返回
Executor
将查询结果写入一级缓存(LocalCache
);- 结果逐层返回:
Executor
→SqlSession
→MapperProxy
→ 开发者。
架构设计亮点与启示
- 模块化与职责单一:三层架构清晰划分职责(基础支持层负责通用能力,核心层负责 SQL 逻辑,接口层负责易用性),便于维护和扩展;
- 插件化设计:通过代理模式支持插件拦截核心组件,无需修改源码即可增强功能(如分页、日志),满足个性化需求;
- 配置驱动:所有核心逻辑基于
Configuration
配置动态生成,无需硬编码(如数据源、SQL 语句、结果映射),灵活性高; - 低侵入与易用性:通过动态代理屏蔽底层细节,开发者只需定义 Mapper 接口和 XML,即可完成数据库操作,降低学习成本。
总结
MyBatis 的架构是 “简单与强大” 的平衡体:三层架构确保核心逻辑的稳定性和扩展性,核心组件(Executor
、StatementHandler
等)的协作实现 SQL 执行的全流程,接口层的动态代理简化开发者调用
v1.3.10