Java 王者修炼手册【MyBatis 篇 - 底层依赖】:吃透 JDBC/MyBatis 核心接口类,掌控全链路整合逻辑

大家好,我是程序员强子。
前面文章我们学习了 Mysql,接下来我们趁热打铁,把 Mybatis 相关的底层原理拿下~
其实早就想写一下 Mybatis 了,毕竟我们 CRUD 根本离不开这个框架~
但是呢,想要学透这个框架,很多前置知识需要了解
JDBC 基础 接口 & 类
MyBatis 基础 接口 & 类
SpringBoot 整合 Mybatis 基础 接口 & 类
PS: 文章接下来 可能会出现大量的陌生的类或者接口,没关系,我们先混个眼熟~
对应一个陌生的接口或者类 ,个人认为最重要的是知道它们的作用
而不需要每行代码这样去研究
如果后面涉及到哪个接口或者方法有疑惑,这个时候就可以深入源码~
强子尽量通俗易懂的解释一下每个类或者接口的作用~
并且类出现的顺序是有前后依赖的,请放心食用~
前言
如果小伙伴对 Mybatis 相关源码比较熟悉,可以跳过本文~
为什么要介绍这么多陌生的类 或者 接口呢?
后面我们要学 Mybatis:
多数据源与动态数据源 机制
插件原理
这些底层原理 会大量出现以下的接口 & 类,所以先梳理一下前置知识点~
JDBC 基础接口 & 类
DriverManager
核心作用
加载驱动:MySQL 8.0+ 可省略 Class.forName("com.mysql.cj.jdbc.Driver"),驱动会通过 SPI 自动注册;
获取连接:最核心方法是 getConnection(),返回 Connection 实例
Connection
是数据库会话句柄,所有 SQL 执行都依赖它;
核心作用
事务管理:默认自动提交(autoCommit=true),关闭后需手动 commit()/rollback();
创建 StatementPreparedStatement:调用 prepareStatement,预编译 SQLStatement: 调用 createStatement ,静态 SQL
PreparedStatement
预编译 SQL 执行器
核心作用
预编译:SQL 模板(如 SELECT * FROM user WHERE id = ?)发送到数据库后预编译,多次执行复用编译结果,性能更高;
防注入:参数通过 setXxx() 绑定,数据库自动转义(如单引号),避免 SQL 注入;
类型安全:支持 setLong()/setString()/setNull() 等方法,适配不同数据类型
ResultSet + ResultSetMetaData
ResultSet:存储查询结果,游标默认指向第一行前,需通过 next() 移动游标遍历;
ResultSetMetaData:动态获取结果集结构,适配不同表结构。
案例
MyBatis 基础接口 & 类
DataSource
JDBC 规范定义的接口(javax.sql.DataSource)
核心作用
连接管理内部维护一个 数据库连接池(如 HikariCP、Druid)负责连接的创建、复用、销毁,避免频繁创建连接导致的性能开销
连接提供:对外暴露 getConnection() 方法,MyBatis 执行 SQL 时,会从 DataSource 中获取连接,执行完成后归还连接
常见实现
HikariCP(默认,性能最优)
Druid(阿里开源,支持监控、防火墙)
C3P0(传统连接池,性能较差)
SqlSession
是一个接口~ 是线程不安全的
后面会介绍 线程安全版的:SqlSessionTemplate
核心作用
Java 程序与数据库之间的会话管理器,相当于操作数据库的总入口
数据库操作:查、增、删、改
事务管理: commit 提交、rollback 回滚、close 关闭会话释放资源
获取 Mapper 接口代理对象 :返回 Mapper 接口的动态代理实例(即 MapperProxy 代理后的对象)
获取配置与执行器:获取 MyBatis 全局配置,获取执行器
简单来说: 所有数据库操作最终都通过 SqlSession 落地
SqlSessionTemplate
线程安全 SqlSession 代理 ,是 Mapper 代理对象执行 SQL 的 核心依赖
核心作用
线程安全封装纯 MyBatis 的 SqlSession 是非线程安全的 SqlSessionTemplate 内部通过 ThreadLocal 绑定当前线程的 SqlSession,确保多线程环境下安全执行。
简化 SqlSession 管理:自动处理 SqlSession 的创建、提交、回滚、关闭,开发者无需手动管理
作为 Mapper 执行入口 Mapper 代理对象的所有方法调用,最终都会委托给 SqlSessionTemplate 由它触发 SqlSession 的 selectOne()、update() 等方法,执行 SQL
SqlSessionFactory
核心作用
存储全局配置:加载并存储 MyBatis 所有配置信息,包括:数据库连接信息(依赖 DataSource);SQL 映射元数据(MappedStatement,来自 Mapper.xml 或 @Select 等注解);全局行为配置(如下划线转驼峰、懒加载、插件等);类型别名、类型处理器等。
创建 SqlSession:提供 openSession() 方法,创建 SqlSession 实例
如何创建
纯 MyBatis 场景:需通过 SqlSessionFactoryBuilder 构建
Spring 整合场景:通过 SqlSessionFactoryBean 构建
SqlSessionFactoryBean
FactoryBean 实现类,专门用于在 Spring 环境中构建 SqlSessionFactory
核心作用
整合 Spring 配置:将 DataSource、MyBatis 配置(如 mybatis.configuration.*)、Mapper 映射路径等,统一封装为 SqlSessionFactory 所需的配置。
触发 MyBatis 初始化容器初始化时,调用 SqlSessionFactoryBean.getObject() 方法底层通过 SqlSessionFactoryBuilder 构建 SqlSessionFactory,并将其注册为 Spring Bean。
支持额外配置 setMapperLocations() 配置 Mapper.xml 扫描路径 setPlugins() 注册 MyBatis 插件 setTypeAliasesPackage() 配置实体类别名包
配置 demo
MappedStatement
每个 MappedStatement 对应 Mapper.xml 中的一个
select 标签
insert 标签
update 标签
delete 标签
一个注解 SQL
来跟强子了解一下这个类的作用~
存储 SQL 元数据
SQL 语句本身:可能是原生 SQL,也可能是带占位符 #{} 的动态 SQL;
SQL 类型:包括 SELECT/INSERT/UPDATE/DELETE;
参数类型: ParameterMap,比如 User 类或 Integer;
结果类型:ResultMap,比如返回 User 类或 List;
其他配置:如是否使用缓存、超时时间、fetchSize 等
是不是觉得很眼熟 平常 xml 里面的 sql,组成部分就是包括以上这些~
为 SQL 执行提供「依据」
MapperProxy 拦截到 Mapper 接口方法调用后,
会根据「接口全类名 + 方法名」找到对应 MappedStatement
传递给 MyBatis 底层的执行器(Executor)
执行器通过 MappedStatement 中的信息,就能知道要执行什么 SQL 传什么参数返回什么结果
BoundSql
运行时最终可执行 SQL 的载体
每次执行 Mapper 方法时,由 MappedStatement 根据实际参数动态生成
具体作用
存储最终可执行 SQL 解析动态 SQL(if/foreach/where 等)后生成的、无动态标签的纯 SQL 字符串不过是包含 ? 占位符
维护参数映射元信息存储 ParameterMapping 集合每个 ParameterMapping 对应一个 #{} 占位符,包含参数名、参数类型、类型处理器等
提供参数值入口通过 getParameterObject() 返回绑定的参数对象(如 @Param 封装的 ParamMap),供参数绑定使用
辅助 JDBC 参数绑定 MyBatis 基于 BoundSql 中的 SQL 和 ParameterMapping,将参数值设置到 PreparedStatement 中
调用链路
MapperProxy.invoke() →
MapperMethod.execute() →
SqlSession.selectOne() → MappedStatement.getBoundSql(parameterObject)
案例
假设 Mapper 接口方法:
XML 中的动态 SQL:
当调用 selectUser(1L, "test") 时,生成的 BoundSql 核心内容:
MapperProxy
小伙伴你们是不是会跟强子一样好奇:
为什么调用无实现类的 Mapper 接口,能注入,也能实现直接调用方法执行 SQL ?? 此处应该有 黑人问号脸.jpg
没错,其中的答案就是: MapperProxy !!!
特点
一个动态代理类,基于 JDK 动态代理实现
负责拦截 Mapper 接口的所有方法调用,将其转化为 SqlSession 的 SQL 执行操作。
PS:
动态代理我们前面文章学过,没有学过的小伙伴回去学习下~
工作流程
获取 UserMapper 的时候(注入 或者 手动调用)
MyBatis 不会创建 UserMapper 的实现类
而是通过 MapperProxyFactory 生成一个 MapperProxy 代理对象(伪装成 UserMapper 实例)
开发者拿到的其实是这个代理对象
当调用 userMapper.getUserById(666) 时
代理对象 MapperProxy 会拦截方法调用
解析方法信息,组成 SQL 的唯一标识(即 MappedStatement 的 id)
提取方法参数:获取传入的参数(比如 666);
找到对应 MappedStatement:根据 SQL 唯一标识,从 MyBatis 全局配置(Configuration)中找到对应的 MappedStatement;
调用 SqlSession 执行 SQL:通过 SqlSession 的 selectOne()/selectList() 等方法,传入 MappedStatement 和参数,执行 SQL 并返回结果。
ParameterHandler
参数处理器,SQL 参数绑定核心
核心作用
解析参数映射元信息:从 BoundSql 中读取 ParameterMapping 集合(每个 ParameterMapping 对应 #{xxx} 占位符,包含参数名、类型、TypeHandler 等);
提取参数值:从 MyBatis 封装的参数对象(如 ParamMap、自定义 User 对象)中,通过反射 / 元对象(MetaObject)提取对应参数值;
类型转换与绑定:通过 TypeHandler 将 Java 类型转换为 JDBC 类型,设置到 PreparedStatement 的对应占位符(?)中;
处理特殊场景:如 null 值、数组 / 集合参数、存储过程输出参数等的绑定逻辑
核心实现类
提供一个默认实现 DefaultParameterHandler
扩展需求通过 TypeHandler 实现
关键特性
依赖 TypeHandler:是 Java 类型与 JDBC 类型转换的核心(如 Long → BIGINT、String → VARCHAR);
兼容多参数场景:无论是 @Param 封装的 ParamMap,还是自定义对象,都能通过 MetaObject 统一提取值;
防 SQL 注入:底层依赖 PreparedStatement.setXxx(),参数自动转义,避免注入风险
ResultSetHandler
结果集处理器,结果映射核心
核心作用
解析结果配置:从 MappedStatement 中读取 resultType/ResultMap/resultSetType 等配置;
遍历结果集:逐行处理 JDBC ResultSet,将每行数据映射为指定的 Java 对象;
处理复杂嵌套结果映射,支持:一对一(association)一对多(collection)鉴别器(discriminator)
资源清理:自动关闭 ResultSet,避免数据库资源泄漏;
适配返回类型:根据配置返回单个对象、List、Map 等不同类型结果。
RowBounds
定义
MyBatis 内置的内存分页(逻辑分页)工具类
核心通过 offset(起始偏移量)和 limit(每页条数)两个参数
在查询结果集层面完成分页截取,无需修改 SQL 语句;
案例
使用局限性
性能瓶颈:RowBounds 是内存分页;数据量超过 1000 条时会导致内存占用高、查询耗时久,生产环境严禁用于大数据量分页;
优先级低于物理分页 MyBatis-Plus 官方明确推荐使用 IPage+PaginationInnerInterceptor(物理分页,改写 SQL 添加 LIMIT/OFFSET)仅在小数据量 / 兼容场景下使用 RowBounds;
不支持复杂分页无法获取总条数、总页数等分页元数据仅能获取当前页数据,需手动查询总条数(selectCount(queryWrapper)),不如 IPage 便捷。
核心实现类
MyBatis 仅提供默认实现 DefaultResultSetHandler
支持所有主流结果映射场景
简单映射
复杂嵌套
批量结果
关键特性
自动映射 vs 手动映射:自动映射(autoMapping=true):按列名 / 别名直接匹配 Java 对象属性名,无需配置 ResultMap;手动映射:按 ResultMap 中 column(数据库列)→ property(Java 属性)的配置精准映射;
嵌套结果映射:通过 association/collection 处理关联查询结果,无需额外 SQL 调用;
延迟加载:支持嵌套结果的懒加载(按需加载关联数据),底层通过 ResultLoaderMap 实现;
依赖 TypeHandler:将 JDBC 类型转换为 Java 类型
SpringBoot + Mybatis 整合基础
DataSourceAutoConfiguration
SpringBoot 自动配置数据库连接池(数据源)的核心类
负责创建 DataSource 实例(数据库连接的核心对象)
拥有数据源,MyBatis 就才能连接数据库
具体作用
读取数据源配置:自动读取 application.yml/properties 中 spring.datasource.* 前缀的配置(如 url、username、password、driver-class-name);
自动创建连接池默认使用 SpringBoot 内置的 HikariCP 连接池也支持 Druid、Tomcat-JDBC 等第三方连接池无需手动配置连接池 Bean
注册数据源到 Spring 容器:将创建好的 DataSource 对象注册为 Spring 单例 Bean,供后续 MyBatis 组件(如 SqlSessionFactory)依赖使用;
配置 demo
MyBatisAutoConfiguration
基于 DataSourceAutoConfiguration 创建的数据源
自动初始化 MyBatis 核心组件
SqlSessionFactory
SqlSessionTemplate
并且注册到 Spring 容器 ~
具体作用
依赖数据源创建 SqlSessionFactory:注入 DataSource,并结合 mybatis.* 配置 ,构建 SqlSessionFactory Bean
**读取 MyBatis 自定义配置 **:比如指定 Mapper.xml 路径、实体类别名包、配置文件位置等
创建线程安全的 SqlSessionTemplate:原生 SqlSession 线程不安全,该类会自动创建 SqlSessionTemplate(SqlSession 的线程安全封装)并注册为 Spring Bean 这是 SpringBoot 中 MyBatis 实际使用的会话对象
支持自定义扩展允许开发者通过自定义 SqlSessionFactoryBean、MyBatisConfigurer 等,覆盖默认配置比如添加分页插件、自定义类型处理器
MapperScannerConfigurer
Spring 提供的 BeanPostProcessor 实现类
纯 Spring 场景下的「手动扫描注册器」
负责扫描项目中的 Mapper 接口,将其注册为 Spring BeanDefinition
需手动配置(XML/JavaConfig)才能生效
核心作用
扫描 Mapper 接口:根据配置的扫描路径,扫描包下所有符合条件的接口(被 @Mapper 标记或在 @MapperScan 路径下)
注册 BeanDefinition:对每个扫描到的 Mapper 接口,创建 BeanDefinition,指定实例化方式 为 MapperFactoryBean 通过 MapperFactoryBean 生成 Mapper 代理对象
注入依赖:在 BeanDefinition 中注入 SqlSessionFactory 或 SqlSessionTemplate,确保 MapperFactoryBean 能生成有效的 Mapper 代理对象
MapperScannerAutoConfiguration
自动扫描指定包下的 Mapper 接口,为每个接口生成 MapperProxy 动态代理对象,并注册到 Spring 容器
SpringBoot 场景下的「自动配置类」,封装 MapperScannerConfigurer 的创建
自动触发(满足条件时),无需手动配置
可以直接 @Autowired 注入 Mapper 接口使用
具体作用
扫描指定包下的 Mapper 接口:读取配置中的 mybatis.mapper-locations 或 @MapperScan 注解指定的包路径,扫描该路径下所有的 Mapper 接口
生成 MapperProxy 代理对象:对每个扫描到的 Mapper 接口,底层通过 MapperProxyFactory 生成 MapperProxy 代理对象
注册代理对象到 Spring 容器:将生成的代理对象注册为 Spring Bean,使得可以直接通过 @Autowired 注入使用
后续可以直接注入 Mapper 接口
总结
DataSourceAutoConfiguration 先执行读取数据源配置创建 DataSource Bean 并注册;
MyBatisAutoConfiguration 依赖 DataSource 创建 SqlSessionFactory 和 SqlSessionTemplate Bean;
MapperScannerAutoConfiguration 扫描指定包下的 Mapper 接口基于 SqlSessionTemplate 生成 MapperProxy 代理对象注册到 Spring 容器;
最终效果:开发者只需注入 Mapper 接口即可
JDBC vs Mybatis
总结
今天我们 复习+回顾了 各种各样的 接口 & 类
首先我们简单回顾一下 **JDBC 相关 **接口 & 类
接下来 我们了解 Mybatis 相关的 接口 & 类
顺便复习 一下 SpringBoot + Mybatis 整合的关键类
最后 JDBC 与 Myabtis 对比 关联
这些对后续我们要搞懂 Mybatis 底层原理非常重要,
所以今天强子费了好大劲,搜集了这些相关的 接口 & 类,对比着学习~
下篇文章强子准备专门把 Mybatis 的以下机制搞定:
多数据源与动态数据源
插件原理
感兴趣的可以关注一下~
如果觉得帮到你们,烦请 点赞关注推荐 三连,感谢感谢~~
熟练度刷不停,知识点吃透稳,下期接着练~
版权声明: 本文为 InfoQ 作者【DonaldCen】的原创文章。
原文链接:【http://xie.infoq.cn/article/fb0515357d28c0031b996654e】。文章转载请联系作者。







评论