# 源码解读

作者: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

按照上述流程分析

  1. 创建工厂类, 进行配置文件的解析, 包括mybatis-config.xml 和 Mapper 映射器文件
  2. 通过 SqlSessionFactory 创建一个 SqlSession
  3. 获得一个Mapper对象
  4. 调用接口方法
  5. 关闭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

# 创建 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

# 获得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
  • 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

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

实际上CachingExecutor为外层包装器, 也就是说Executor.query() 总是先执行CachingExecutor.query的实现, 内部会判断如果不存在二级缓存再走BaseExecutor.query()

调用 Executor.query() -> 调用 CachingExecutor.query()
    ├─ 判断是否有二级缓存,如果有且命中,返回缓存数据
    └─ 否则调用 BaseExecutor.query()
          ├─ 先查一级缓存,如果有,返回一级缓存数据
          └─ 否则执行数据库查询

1
2
3
4
5
6

# 总结

  • MyBatis 配置文件解析过程涉及多步组件初始化,最终生成 ConfigurationDefaultSqlSessionFactory
  • 创建 SqlSession 时,加载环境配置,开启事务,创建执行器。
  • 获取 Mapper 代理对象,JDK 动态代理封装接口方法调用。
  • 通过 MapperProxy.invoke() 实现方法拦截,调用 Executor 执行 SQL 并返回结果。
  • 缓存机制由 CachingExecutor 管理,先查二级缓存,再查一级缓存,未命中时执行数据库查询。