# 源码解读
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
相关源码参考: MyBatis源码分析代码仓库 (opens new window)
默认读者已知并熟练使用MyBatis
代码fork mybatis-3, 旧代码仓库废弃使用
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
xxxMapper mapper = session.getMapper(xxxMapper.class);
xxx xxx = mapper.selectXxxById(1);
session.close();
1
2
3
4
5
6
2
3
4
5
6
按照上述流程分析
- 创建工厂类, 进行配置文件的解析, 包括mybatis-config.xml 和 Mapper 映射器文件
- 通过 SqlSessionFactory 创建一个 SqlSession
- 获得一个Mapper对象
- 调用接口方法
- 关闭session
# 配置文件解析
MyBatis 主要解析 mybatis-config.xml 配置文件和各个 Mapper 映射文件。
MyBatis 配置文件解析流程树(以 SqlSessionFactoryBuilder 为起点)
SqlSessionFactoryBuilder
├── build(reader / inputStream, env, props) → 创建 XMLConfigBuilder 解析器(解析 mybatis-config.xml)
│ └── 内部创建 Configuration 实例(全局配置容器)
│
├── 调用 XMLConfigBuilder.parse() → 解析 <configuration> 根节点
│ └── parseConfiguration(XNode root)
│ ├── propertiesElement → 加载外部配置,合并属性到 configuration.variables
│ ├── settingsElement → 转换为 Properties,反射设置到 Configuration 对象
│ ├── typeAliasesElement → 注册类型别名到 TypeAliasRegistry
│ ├── pluginElement → 创建插件实例,添加到 configuration.interceptorChain
│ ├── objectFactoryElement → 配置对象工厂
│ ├── objectWrapperFactoryElement → 配置对象包装器
│ ├── reflectorFactoryElement → 配置反射器工厂
│ ├── environmentsElement → 按 environment ID 选择数据源配置
│ │ ├── transactionManagerElement → 创建 TransactionFactory
│ │ └── dataSourceElement → 创建 DataSourceFactory & DataSource 实例
│ ├── databaseIdProviderElement → 获取当前数据库 ID
│ ├── typeHandlerElement → 注册类型处理器
│ └── mappersElement → 注册 Mapper 文件和接口
│ ├── 解析每个 mapper.xml 文件(XMLMapperBuilder.parse())
│ └── 绑定 namespace 到 Mapper 接口(Configuration.addMapper())
│
└── 构建 DefaultSqlSessionFactory(Configuration)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 创建 SqlSession(会话)
配置文件解析完成,生成 DefaultSqlSessionFactory,通过 openSession() 创建数据库会话。
SqlSessionFactory.openSession()
├── DefaultSqlSessionFactory.openSession()
│ ├── openSessionFromDataSource(ExecutorType.SIMPLE, null, false)
│ │ ├── configuration.getEnvironment() → 获取环境配置
│ │ ├── getTransactionFactoryFromEnvironment(environment) → 获取事务工厂
│ │ │ └── environment.getTransactionFactory()
│ │ ├── transactionFactory.newTransaction(dataSource, level, autoCommit) → 创建事务,获取 JDBC Connection
│ │ ├── configuration.newExecutor(tx, execType) → 创建 Executor(默认 SimpleExecutor)
│ │ │ ├── 若启用二级缓存,包装成 CachingExecutor
│ │ └── new DefaultSqlSession(configuration, executor, autoCommit) → 创建 SqlSession 实例
│ └── 返回 SqlSession
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 获得Mapper对象
通过 SqlSession.getMapper(Mapper.class) 获取 Mapper 接口的代理实现。
SqlSession.getMapper(Class<T> type)
├── DefaultSqlSession.getMapper(type)
│ └── configuration.getMapper(type, this)
│ └── mapperRegistry.getMapper(type, sqlSession)
│ ├── knownMappers.get(type) → 获取 MapperProxyFactory
│ ├── mapperProxyFactory.newInstance(sqlSession)
│ │ ├── new MapperProxy<>(sqlSession, type)
│ │ └── Proxy.newProxyInstance(...) → JDK 动态代理对象
│ │ └── 代理对象调用时进入 MapperProxy.invoke()
│ └── 返回代理对象
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- MapperProxy 实现了
InvocationHandler,负责拦截接口方法调用并执行对应 SQL。 - 每次调用 Mapper 接口方法,最终都会转到
MapperProxy.invoke()。
# 执行 SQL 逻辑
调用 Mapper 方法后,实际执行流程如下:
MapperProxy.invoke(proxy, method, args)
├─ 如果是 Object 类方法,直接调用原方法
├─ 如果是 Java 8 默认方法,通过反射调用
├─ 否则调用缓存的 MapperMethodInvoker.invoke()
│ └─ MapperMethod.execute(sqlSession, args)
│ ├─ 根据 SQL 命令类型(SELECT/INSERT/UPDATE/DELETE)
│ ├─ 调用 sqlSession 的对应方法(selectOne/selectList/insert/update/delete)
│ ├─ 处理返回类型(单条、集合、Map、Optional等)
│ └─ 返回执行结果
└─ 处理异常并抛出
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
以 select 查询为例:
execute(sqlSession, args)
├─ 根据 command.getType()
│ ├─ SELECT
│ │ ├─ 如果返回多条,调用 sqlSession.selectList()
│ │ └─ 否则调用 sqlSession.selectOne()
│ ├─ INSERT/UPDATE/DELETE 等其他类型
│
sqlSession.selectList(statementId, param)
├─ 调用 Executor.query()
│ ├─ 先检查二级缓存(CachingExecutor)
│ ├─ 若未命中,再检查一级缓存(Local Cache)
│ ├─ 缓存未命中,执行数据库查询
│ └─ 查询结果返回并放入缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
实际上CachingExecutor为外层包装器, 也就是说Executor.query() 总是先执行CachingExecutor.query的实现, 内部会判断如果不存在二级缓存再走BaseExecutor.query()
调用 Executor.query() -> 调用 CachingExecutor.query()
├─ 判断是否有二级缓存,如果有且命中,返回缓存数据
└─ 否则调用 BaseExecutor.query()
├─ 先查一级缓存,如果有,返回一级缓存数据
└─ 否则执行数据库查询
1
2
3
4
5
6
2
3
4
5
6
# 总结
- MyBatis 配置文件解析过程涉及多步组件初始化,最终生成
Configuration和DefaultSqlSessionFactory。 - 创建 SqlSession 时,加载环境配置,开启事务,创建执行器。
- 获取 Mapper 代理对象,JDK 动态代理封装接口方法调用。
- 通过
MapperProxy.invoke()实现方法拦截,调用 Executor 执行 SQL 并返回结果。 - 缓存机制由
CachingExecutor管理,先查二级缓存,再查一级缓存,未命中时执行数据库查询。