0%

mybatis构成

MyBatis 架构与核心组件全解析:从三层架构到 SQL 执行链路

MyBatis 的架构设计清晰且模块化,通过 基础支持层、核心处理层、接口层 三层结构实现 “配置驱动、插件可扩展、低侵入” 的特性。本文深入拆解每一层的核心组件、职责及协作关系,并结合 SQL 执行全链路,帮你理解 MyBatis 如何将 “配置” 转化为 “数据库操作”,构建完整的技术认知。

MyBatis 三层架构总览

MyBatis 的三层架构并非物理隔离,而是按 “职责划分” 的逻辑分层,各层组件协同工作,形成从 “接口调用” 到 “数据库响应” 的闭环。三层的核心职责与组件对应关系如下:

架构分层 核心职责 关键组件
接口层 提供对外 API,简化开发者调用 SqlSessionMapperProxy(动态代理)
核心处理层 实现 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 类型的双向转换 - 内置类型:StringTypeHandlerIntegerTypeHandler 等(覆盖常用类型); - 自定义扩展:实现 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:存储注册的插件(拦截器链)。
  • 地位:所有核心组件(ExecutorStatementHandler 等)的创建都依赖 Configuration,是组件协作的 “数据中枢”。
Executor:执行器(SQL 调度核心)
  • 职责:MyBatis 的 “执行调度中心”,负责协调 StatementHandlerParameterHandlerResultSetHandler 完成 SQL 执行,同时管理一级缓存和事务;

  • 核心类型(根据execType配置):

    | 执行器类型 | 特点 | 适用场景 |
    | ————————- | —————————————————————- | ——————————————— |
    | SimpleExecutor | 默认实现,每次执行 SQL 都创建新 Statement | 单条 SQL 执行(大多数场景) |
    | ReuseExecutor | 复用 Statement(按 SQL 缓存) | 重复执行相同 SQL(如循环查询) |
    | BatchExecutor | 批量执行 SQL(仅支持 INSERT/UPDATE/DELETE) | 批量操作(如批量插入) |
    | CachingExecutor | 装饰器模式,添加二级缓存支持 | 开启二级缓存时自动生效 |

  • 核心流程:

    1. 接收 SqlSession 的请求(如 selectOne);
    2. Configuration 中获取 MappedStatement
    3. 创建 StatementHandler 并委托其执行 SQL;
    4. 管理一级缓存(查询前查缓存,查询后写缓存)。
StatementHandler:SQL 执行处理器(与数据库交互)
  • 职责:直接与数据库交互,负责创建 Statement(JDBC 对象)、预编译 SQL、执行 SQL 并返回 ResultSet,是 “承上启下” 的关键组件;

  • 核心类型(根据statementType配置):

    | 处理器类型 | 对应 JDBC 对象 | 特点 |
    | ————————————— | —————————- | —————————————————— |
    | SimpleStatementHandler | Statement | 无参数 SQL(不支持 ? 占位符) |
    | PreparedStatementHandler | PreparedStatement | 预编译 SQL(支持参数占位符,防注入) |
    | CallableStatementHandler | CallableStatement | 调用存储过程(支持 IN/OUT 参数) |

  • 核心流程:

    1. Executor 接收 MappedStatement 和参数;
    2. 调用 Connection.prepareStatement() 预编译 SQL;
    3. 委托 ParameterHandler 设置 SQL 参数;
    4. 执行 SQL(executeQuery()/executeUpdate()),返回 ResultSet
    5. 委托 ResultSetHandler 处理结果。
ParameterHandler:参数处理器
  • 职责:将 Mapper 方法的参数(如 user.getId())映射到预编译 SQL 的 ? 占位符,解决 “Java 参数 → JDBC 参数” 的类型转换;
  • 核心逻辑:
    1. BoundSql 中获取参数映射列表(parameterMappings,记录参数名、Java 类型、JDBC 类型);
    2. 通过 TypeHandler 将 Java 参数值转换为 JDBC 类型(如 LocalDateTimeTimestamp);
    3. 调用 PreparedStatement.setXxx() 方法设置参数(如 setInt()setString())。
  • 注意:MyBatis 内置的 DefaultParameterHandler 已覆盖 99% 的场景,无需自定义,除非有特殊类型转换需求。
ResultSetHandler:结果集处理器
  • 职责:将 JDBC 的 ResultSet(数据库返回结果)解析为 Java 实体类(或 List/Map),解决 “JDBC 结果 → Java 对象” 的映射;
  • 核心逻辑:
    1. MappedStatement 中获取 ResultMap(结果映射规则);
    2. 遍历 ResultSet 的每一行数据;
    3. 根据 ResultMap 匹配 “数据库列名 → 实体属性名”,通过 TypeHandler 转换类型(如 TimestampLocalDateTime);
    4. 调用实体类的 setter 方法设置属性,或通过构造器注入(constructor 映射);
    5. 处理关联查询(association/collection):触发嵌套查询或解析嵌套结果。
  • 关键能力:支持自动映射(autoMapping)、驼峰命名映射(mapUnderscoreToCamelCase)、复杂关联映射,是 MyBatis 结果处理的核心。
Plugin:插件(拦截器)
  • 职责:基于代理模式,拦截核心组件的方法(如 Executor.query()StatementHandler.prepare()),实现功能增强(如分页、日志、性能监控);
  • 核心原理:
    1. 插件实现 Interceptor 接口,通过 @Signature 注解指定拦截的组件和方法;
    2. MyBatis 初始化时,将插件注册到 Configuration.plugins
    3. 创建核心组件(如 Executor)时,通过 Plugin.wrap() 生成代理对象;
    4. 调用组件方法时,触发插件的 intercept() 方法,执行增强逻辑。
  • 常见应用:PageHelper 分页插件(拦截 Executor.query() 改写 SQL)、日志插件(拦截 StatementHandler 打印 SQL)。

3. 接口层:开发者的 “入口”

接口层是 MyBatis 对外暴露的 API,旨在简化开发者调用,屏蔽底层复杂逻辑(如 ExecutorStatementHandler 的协作)。

(1)核心组件与职责
SqlSession:数据库会话
  • 职责:相当于 JDBC 的 Connection,代表一次数据库会话,提供 CRUD 方法(selectOneinsertupdatedelete),是开发者直接操作的入口;
  • 核心实现DefaultSqlSession(默认实现),持有 ConfigurationExecutor,将方法调用委托给 Executor
  • 生命周期:单次业务请求(线程不安全,需及时关闭,避免连接泄漏)。
MapperProxy:Mapper 接口动态代理
  • 职责:MyBatis 不要求开发者实现 Mapper 接口,而是通过 JDK 动态代理生成 MapperProxy 对象,将 Mapper 方法调用转发给 SqlSession
  • 核心逻辑:
    1. 调用 Mapper 方法(如 userMapper.selectById(1))时,触发 MapperProxy.invoke()
    2. 解析方法信息(如 namespace + methodName),找到对应的 MappedStatement
    3. 调用 SqlSession 的对应方法(如 sqlSession.selectOne()),完成 SQL 执行;
  • 优势:无侵入式开发,开发者只需定义 Mapper 接口和 XML,无需编写实现类。

SQL 执行全链路:三层组件协作实战

以 “调用 userMapper.selectById(1) 查询用户” 为例,完整拆解三层组件的协作流程,理解 MyBatis 如何将 “接口调用” 转化为 “数据库响应”:

1. 链路总览

2. 关键步骤拆解(结合组件职责)

步骤 1:接口调用触发动态代理
  • 开发者注入 UserMapper 接口,实际注入的是 MapperProxy 代理对象;
  • 调用 userMapper.selectById(1) 时,触发 MapperProxy.invoke() 方法。
步骤 2:解析方法并委托 SqlSession
  • MapperProxy 解析方法的 namespacecom.xxx.UserMapper)和 methodNameselectById),拼接为 MappedStatementidcom.xxx.UserMapper.selectById);
  • 调用 sqlSession.selectOne("com.xxx.UserMapper.selectById", 1),将请求委托给 SqlSession
步骤 3:Executor 调度核心组件
  • SqlSessionDefaultSqlSession)调用 executor.query(ms, parameter, rowBounds, resultHandler)
  • Executor(如 CachingExecutor)先查询二级缓存,未命中则调用 SimpleExecutorquery()
  • SimpleExecutor 创建 StatementHandlerPreparedStatementHandler),并传入 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 规则(如 ididuser_nameuserName),通过 TypeHandler 转换类型;
  • 创建 User 对象,调用 setter 方法设置属性,返回 User 列表。
步骤 6:缓存与结果返回
  • Executor 将查询结果写入一级缓存(LocalCache);
  • 结果逐层返回:ExecutorSqlSessionMapperProxy → 开发者。

架构设计亮点与启示

  1. 模块化与职责单一:三层架构清晰划分职责(基础支持层负责通用能力,核心层负责 SQL 逻辑,接口层负责易用性),便于维护和扩展;
  2. 插件化设计:通过代理模式支持插件拦截核心组件,无需修改源码即可增强功能(如分页、日志),满足个性化需求;
  3. 配置驱动:所有核心逻辑基于 Configuration 配置动态生成,无需硬编码(如数据源、SQL 语句、结果映射),灵活性高;
  4. 低侵入与易用性:通过动态代理屏蔽底层细节,开发者只需定义 Mapper 接口和 XML,即可完成数据库操作,降低学习成本。

总结

MyBatis 的架构是 “简单与强大” 的平衡体:三层架构确保核心逻辑的稳定性和扩展性,核心组件(ExecutorStatementHandler 等)的协作实现 SQL 执行的全流程,接口层的动态代理简化开发者调用

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10