Mybatis Source
理解 Mybatis 的源码
测试示例
理解Mybatis
结构上理解
Configuration
Mybatis 中 Configuration 更好的理解上将其理解为 配置 + 解析
- 存储配置信息
- 一系列的
boolean变量(safeRowBoundsEnabled,safeResultHandlerEnabled,mapUnderscoreToCamelCase,...),用来控制CURD的input和output -
存储 环境信息
Environment -
存储解析信息
- 通过 MapperRegistry 缓存了 MapperProxyFactory
- 存储 InterceptorChain 过滤链
- 存储 TypeHandlerRegistry
- 存储 TypeAliasRegistry,
- 通过 HashMap (
Map<String, MappedStatement> mappedStatements) 存储了MappedStatement- 一个 MappedStatement 对应 Mapper.xml 中的一个
INSERT,SELECT,DELETE,UPDATE的代码片段
- 一个 MappedStatement 对应 Mapper.xml 中的一个
- 通过 HashMap(
Map<String, Cache>) 存储了 Mybatis 的缓存 - 通过 HashMap(
Map<String, ResultMap>) 存储了 Mapper.xml 中 ResultSet 块 - 通过 HashMap(
Map<String, ParameterMap>) 存储了 Mapper.xml 中 ParameterMap 块 - 通过 HashMap(
Map<String, KeyGenerator>) 存储了KeyGenerator
Environment
Mybatis定义的环境,用途理解: 这个环境和JDBC有紧密的关联
-
包含了 TransactionFactory,用来创建事务
-
包含了 DataSource,用来获取连接
MapperRegistry
从多类框架源码中可以发现,一般
Registry结尾的都是在框架的初始化阶段将需要的信息存储进去
-
引用了
Configuration -
一对一映射了 Class 和 MapperProxyFactory
Class: Java代码书写的 Mapper 接口MapperProxyFactory: 生成动态代理抽象的工厂,用来生成MapperProxy
MapperProxy
真正执行Mapper接口方法的关键类
- 包含了
SqlSession sqlSession: 一次JDBC执行抽象Session - 包含了
Class<T> mapperInterface: 对应了 Java编写的Mapper接口 - 包含了
Map<Method, MapperMethod> methodCache: 接口里的每个方法对应了 MapperMethod
创建Mapper接口代理对象的 核心是 MapperProxy 实现了 InvocationHandler
MapperMethod
从名字就可看出 和 Java的Mapper和Xml的Mapper 有关
- 包含了
SqlCommand commandSqlCommand是MappedStatement的引用映射,SqlCommand主要记录了 id 和 type - id 的 作用是从Configuration缓存中找到内容
- type是CURD的类型,在真实执行的时候再分发一次
- 包含了
MethodSignature method,MethodSignature是对接口方法的抽象 - 枚举了返回类型,用boolean表示选择,记录下返回的类类型
- 引用参数处理器
Mapper接口方法的唯一定义 MapperMethod = Mapper.class + Mapper.method()
MapperMethod在整个Mybatis框架中关联了 Java的接口 和 Xml文件的标签
MappedStatement
与书写的Mapper.xml文件相关
对于某个 XXXMapper.xml
- 其中一定要包含namespace, 指向了Java代码中Mapper的接口
- 对于
SELCET|INSERT|UPDATE|DELETE,每一块儿解析成MappedStatement
DefaultSqlSessionFactory
从多类框架源码中可以发现, 一般
Factory结尾的都是用来生成对象使用的,所以这个是用来产生SqlSession的
- 引用了
Configuration -
深层理解 : 引用了
Environment -
implements SqlSessionFactory
SqlSessionManager
关键是自己定义的方法
-
引用了
Configuration -
implements SqlSessionFactory, 通过观察成员变量存在SqlSessionFactory sqlSessionFactory,说明这里是代理 -
implements SqlSession, 表明SqlSessionManager本身就是个SqlSession -
成员变量声明了
ThreadLocal<SqlSession> localSqlSession, 一般和多线程相关 -
综上从结构上理解,
SqlSessionManager作为Factory可以产生SqlSession, 作为SqlSession可以执行, 因为其又包含了ThreadLocal,所以这个SqlSessionManager暂时理解为是在多个Java方法有SQL调用的时候控制事务使用的
DefaultSqlSession
从SqlSession接口上看, 提供了通用的curd方法, 一般不直接调用。
提供了getMapper()的方法, 这个就是Mybatis提供的ORM
- 引用了
Configuration, 等价于引用了Environment, 等价于可以获取到DataSource和Transaction - 定义了执行器:
Executor executor,真实的JDBC调用封装在这个里面
行为上理解
创建 SqlSessionFactory
理解 : SqlSessionFactory 是最终产生 SqlSession 的地方,在创建 SqlSessionFactory
的时候,需要抓住的关键点是在创建过程中基于哪些外部配置文件创建了内部的配置项
这些内部的配置项(Configuration和Environment)是内部逻辑读取外部配置并构建,从SqlSessionFactory的
openSession()方法上可以看出内部的配置是不需要暴露出来的
小结: 这种设计是大部分框架的设计思路,屏蔽不必要的配置细节,通用化配置过程
创建 SqlSessionFactory 的内部有个读取xml文件并解析的工具 XMLConfigBuilder, 这个类继承自 BaseBuilder。
BaseBuilder
- 声明了
Configuration,TypeAliasRegistry,TypeHandlerRegistry,关键修饰protected,说明子类可以直接使用 TypeAliasRegistry和TypeHandlerRegistry是引用的Configuration自身包含的
Mybatis自身规划了很多 Builder(查看 BaseBuilder的继承) 去解析各类配置并存储到 Configuration 中
对于Mapper接口和Mapper.xml文件的解析是在解析Xml文件中的<mapper>标签再次细化
- 细化出
MapperProxyMapperMethodMapperStatement
Q & A :
Q: Mybatis是屏蔽不必要的配置细节,通用化配置过程的?
A: Mybatis提供一个XML配置文件,通过读取XML配置文件,初始化了相应的Configuration和Environment
获取 SqlSession
sqlSessionFactory.openSession()
与Datasource以及Transaction相关
SqlSession 本身的抽象是对 JDBC 连接以及执行的抽象聚合
- 引用了
Configuration本质上可以传递了初始化的缓存数据,关键是使用Configuration中的Environment数据 - 从上面的结构可以了解,
Environment主要包含了TransactionFactory和DataSource - 在构建
SqlSession的时候 构建了Executor包含了Transaction - 真正的SQL执行是在Executor中
获取接口代理对象
Proxy.newProxyInstance: JDK的动态代理
对于Mapper而言,无论是接口Mapper还是文件XMLMapper,都被抽象成了MapperProxy
执行细节的抽象就是MapperMethod
执行接口里面的方法 MapperProxy MapperMethod
这里是分发执行就是Mapper接口方法的关键
MapperProxy
观察MapperProxy真实的调用思路
- 从
Map<Method, MapperMethod> methodCache获取到 Java的Mapper接口映射的MapperMethod方法 - 由
MapperMethod中的Object execute(SqlSession sqlSession, Object[] args)这个方法去执行JDBC逻辑 - 方法的入参
SqlSession sqlSession, 与Sql执行和事务有关 - 方法的入参
Object[] args, 对于接口方法里的参数,Mybatis会将其转换成完整逻辑需要的参数 - 方法的出参 用
Object接收,对应InvocationHandler中invoke的返回- 对于出参,在JDBC中,返回的结果类型是可枚举的, 所以对每种返回的结果做好映射转换即可
INSERT,UPDATE,DELETE一般返回影响的行数SELECT返回的基于ResultSet可以转换为 单个对象 或者 对象集合- 如果本身接口方法是
void没有返回值,return null即可
MapperMethod
对外暴露的执行方法 Object execute(SqlSession sqlSession, Object[] args)
方法执行逻辑
- 枚举
SqlCommand的类型(INSERT|UPDATE|DELETE|SELECT|FLUSH), 分发执行类型 SELECT的执行又细化了returnManyreturnMapreturnsCursorreturnOne- 对于影响行数的返回单独处理, (
int|Integer|long|Long|boolean|Boolean) boolean是业务封装 - 对于接口方法为
void的处理是最简单的,return null即可、 - 真实的执行还是由
SqlSession来执行的,SqlSession在框架中是距离JDBC最近的抽象封装 SqlSession执行的本质是Executor<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)
Executor
真正执行JDBC操作的入口
理解Java对于JDBC事务的封装
可以扩展思考到Spring的事务设计
Transaction的设计