MyBatis整合Spring全流程解析
本文概览:本文深入解析MyBatis与Spring的整合全流程,从原生MyBatis执行流程入手,逐步剖析SqlSessionFactoryBean、MapperScannerConfigurer等核心整合机制,揭示Spring如何接管MyBatis组件的生命周期管理
原生 MyBatis 的执行流程
在未整合 Spring 时,MyBatis 的使用需要手动管理所有资源:
- 创建 SqlSessionFactory(工厂)首先需要加载 MyBatis 配置文件,通过 SqlSessionFactoryBuilder 构建一个 SqlSessionFactory 对象。这个工厂是单例的,负责创建后续的 SqlSession,整个应用生命周期内只需要初始化一次
- 获取 SqlSession(数据库会话窗口)每次需要操作数据库时,都要通过工厂的 openSession() 方法手动创建一个 SqlSession。SqlSession 代表一次与数据库的会话,内部封装了数据库连接、执行器和事务管理器。它是非线程安全的,每次数据库操作都需要独立创建
- 获取 Mapper 代理对象通过 SqlSession 的 getMapper() 方法,传入 Mapper 接口的 Class 对象,MyBatis 会利用动态代理技术为这个接口生成一个实现类。这个代理对象内部持有 SqlSession 的引用,但本身不包含业务逻辑
- 执行并释放资源调用 Mapper 代理对象的方法执行数据库操作后,必须手动提交事务(commit)或回滚(rollback),最后手动关闭 SqlSession释放数据库连接。如果忘记关闭,会导致连接泄漏
Spring 整合 MyBatis 后的流程
整合后,Spring 接管了所有资源管理,开发者只需关注业务逻辑:
- Spring 启动时自动初始化应用启动时,Spring 通过扫描注解自动发现所有的 Mapper 接口,为每个接口创建对应的代理对象并注册到 Spring 容器中。同时,SqlSessionFactory 也被创建为 Spring 的单例 Bean
- 调用 Mapper 方法时自动处理开发者通过依赖注入获得 Mapper 接口的引用(实际上是代理对象),直接调用方法即可。这个代理对象在方法被调用时,会自动从 Spring 的事务上下文中获取 SqlSession。如果当前存在事务,则复用已绑定的 SqlSession;如果没有事务,则临时创建一个新的
- SqlSession 的自动化管理Spring 使用 SqlSessionTemplate 对 SqlSession 进行封装,这是一个线程安全的代理。它内部会拦截所有 SqlSession 的操作,自动处理事务的提交或回滚、异常转换为 Spring 统一的异常体系,并在操作完成后自动关闭 SqlSession,避免资源泄漏
Mybatis数据库操作的完整流程
第 1 步:应用启动时 —— 构建核心基础设施
- Spring 创建DataSourceBean通常由 Spring Boot 自动配置(如 HikariCP、Druid)负责管理数据库连接池,提供java.sql.Connection
- Spring 创建SqlSessionFactoryBean@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource); // ← 关键绑定!
return factory.getObject();
}SqlSessionFactory持有对DataSource的引用加载所有 Mapper XML / 注解,解析为MappedStatement,存入内部的Configuration对象此时SqlSessionFactory已具备“执行任意 SQL”的能力,但尚未创建连接 - Spring 扫描 Mapper 接口,注册代理 Bean通过@MapperScan,MyBatis-Spring 为每个接口生成一个MapperFactoryBean该 FactoryBean 在getObject()时返回一个动态代理对象(MapperProxy)这个代理对象内部持有SqlSessionTemplate(而SqlSessionTemplate持有SqlSessionFactory)
至此,依赖链建立完成:
Mapper Proxy → SqlSessionTemplate → SqlSessionFactory → DataSource
第 2 步:业务代码调用 Mapper 方法
1 | User user = userMapper.selectById(1L); |
→ 实际调用的是JDK 动态代理的invoke()方法
第 3 步:MapperProxy.invoke()拦截调用
- 将方法调用封装为MapperMethod
- 调用mapperMethod.execute(sqlSession, args)
- 这里的sqlSession是SqlSessionTemplate(线程安全的代理)
第 4 步:SqlSessionTemplate获取真正的SqlSession
- SqlSessionTemplate不是真正的SqlSession,而是一个代理/装饰器;
- 作用:检查当前线程是否有 Spring 管理的事务(TransactionSynchronizationManager)如果有 → 复用已存在的SqlSession(保证事务一致性)如果没有 → 调用sqlSessionFactory.openSession()创建新会话
- 关键点:sqlSessionFactory.openSession()内部会:创建一个Transaction(如SpringManagedTransaction或JdbcTransaction)该 Transaction 从DataSource中获取一个Connection将Connection封装进Executor,再包装成DefaultSqlSession
此刻,SqlSession才真正拥有了数据库连接!
第 5 步:执行 SQL(通过DataSource提供的连接)
- SqlSession.selectOne("com.example.UserMapper.selectById", 1)
- 根据 ID 从Configuration中找到对应的MappedStatement
- 使用PreparedStatementHandler执行 SQL:Connection conn = transaction.getConnection(); // ← 来自 DataSource!
PreparedStatement ps = conn.prepareStatement(sql);
// 设置参数、执行、获取 ResultSet... - 结果集通过ResultMap映射为 Java 对象(如User)
第 6 步:返回结果 & 释放资源
- 如果是非事务方法:SqlSessionTemplate在方法结束后自动关闭SqlSessionConnection被归还给DataSource的连接池
- 如果是@Transactional方法:Connection保持打开,直到事务提交/回滚事务结束时,Spring 通知DataSource归还连接
全局视角:各组件角色总结
| 组件 | 角色 | 在流程中的作用 |
|---|---|---|
| DataSource | 数据库连接提供者 | 提供Connection,决定连接池、URL、账号等;所有 SQL 最终通过它执行 |
| SqlSessionFactory | SqlSession工厂 | 持有DataSource+Configuration(含所有 SQL 映射);负责创建SqlSession |
| SqlSession | SQL 执行器 | 持有Connection,执行具体 SQL,管理本地缓存、事务状态 |
| Mapper Proxy | 接口调用桥梁 | 将userMapper.selectById()转为sqlSession.selectOne(...) |
| Spring | 协调者 | 管理 Bean 生命周期、事务、线程绑定,确保SqlSession与DataSource正确协作 |
核心对比
| 环节 | 原生 MyBatis | Spring 整合后 |
|---|---|---|
| SqlSessionFactory | 手动创建单例 | Spring 容器自动管理 |
| SqlSession 获取 | 手动 openSession() | 自动从事务上下文获取 |
| Mapper 代理 | 手动 getMapper() | Spring 自动注入 |
| 事务管理 | 手动 commit/rollback | Spring 声明式事务管理 |
| 资源释放 | 手动 close() | Spring 自动关闭 |
| 异常处理 | MyBatis 原生异常 | 自动转为 Spring 统一异常 |
版权声明:本文为CSDN博主「青山木」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LI_124301012/article/details/159828465
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 青山木!




