Mybatis源码分析
- Mybatis源码分析入口
- 1. 读取配置文件
- 总结
- 2. 解析配置文件
- 核心代码(一)
- 核心代码(二)
- 分析parse()方法
- 分析build()方法
- 总结
- 3. 获取SqlSession
- 总结
- 4. 获取mapper代理对象
- 总结
- 5. 使用mapper代理对象执行Sql语句
- 二级缓存
- 一级缓存
- 总结
Mybatis源码分析入口
本文将根据下面这段代码进行源码分析
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserEntity> list = mapper.listUser();
System.out.println(list);
sqlSession.close();
1. 读取配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
ClassLoaderWrapper.java
总结
从入口一路点击进去可以发现底层是通过调用java.lang.ClassLoader#getResourceAsStream方法来读取resources目录下的mybatis-config.xml文件,并得到InputStream对象
2. 解析配置文件
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder.java
核心代码(一)
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
XMLConfigBuilder.java
XPathParser.java
可以发现底层是将InputStream对象转换成Document对象,并将Document对象保存至当前类(XPathParser)的document属性中
继续回到上一层,点击进入this()方法
XMLConfigBuilder.java
可以发现this()方法主要是在进行部分属性的初始化,并将XPathParser对象保存至当前类(XMLConfigBuilder)的parser属性中。
关键点:初始化了父类的configuration属性。
核心代码(二)
return build(parser.parse());
分析parse()方法
XMLConfigBuilder.java
- 根据Document对象获取节点为configuration的配置信息,并转换成XNode对象
XPathParser.java
- 将XNode对象解析成Configuration对象
XMLConfigBuilder.java
XMLConfigBuilder.java
点击进入addMappers(mapperPackage)方法
Configuration.java
MapperRegistry.java
从这里可以发现parser.parse()主要是在解析配置文件(mybatis-config.xml),具体过程是根据Document对象获取节点为configuration的配置信息,并转换成XNode对象再解析各个节点,重点部分是mappers节点的解析。
在解析mappers节点的代码中可以发现如果是使用package或class注册mapper可以直接注册mapper接口对象,如果是使用url或者resource注册mapper则需要先解析mapper.xml映射文件后并通过namespace找到所绑定的接口对象再进行注册。
mapper的注册是通过MapperRegistry对象完成的,而MapperRegistry则是Configuration对象里面的一个属性,也就是说所有的配置解析完成后都存放在Configuration对象中。
parser.parse()最终返回Configuration对象。
分析build()方法
SqlSessionFactoryBuilder.java
DefaultSqlSessionFactory.java
从这里可以发现SqlSessionFactoryBuilder将得到的Configuration对象建造成DefaultSqlSessionFactory对象,也就是SqlSessionFactory对象。
总结
SqlSessionFactoryBuilder先是通过XMLConfigBuilder解析配置文件并将解析得到的配置装载到Configuration对象中,再将Configuration建造成DefaultSqlSessionFactory对象。
这里采用了建造者设计模式
BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供一些通用的方法;
XMLConfigBuilder:主要负责解析mybatis-config.xml文件;
XMLMapperBuilder:主要负责解析mapper.xml文件;
XMLStatementBuilder:主要负责解析映射文件中的SQL节点;
Configuration对象核心属性释义:
- MapperRegistry:mapper接口动态代理工厂类的注册中心;
- ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id、result等子元素;
- MappedStatement:用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性;
- SqlSource:用于创建BoundSql,mapper.xml文件中的sql语句会被解析成BoundSql对象,经过解析BoundSql包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;
3. 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
DefaultSqlSessionFactory.java
Configuration.java
从这里可以看到如果没有设置执行器类型,则会默认使用简单执行器类型
ExecutorType.java
上面枚举类中的三种执行器类型均可通过openSession()传参设置
点击进入openSessionFromDataSource()方法
DefaultSqlSessionFactory.java
openSessionFromDataSource()方法有三个入参:ExecutorType execType(执行器类型)、TransactionIsolationLevel level(事务隔离级别)、boolean autoCommit(是否自动提交)
- 获取TransactionFactory
DefaultSqlSessionFactory.java
TransactionFactory有两种:JdbcTransactionFactory,ManagedTransactionFactory
通过mybatis-config.xml文件进行配置
<transactionManager type="JDBC"/>
这里配置的是JdbcTransactionFactory
- 获取Transaction
JdbcTransactionFactory.java
JdbcTransaction.java
- 根据Transaction和执行器类型获取执行器(核心代码)
Configuration.java
CachingExecutor.java
Mybatis默认使用的执行器是SimpleExecutor,SimpleExecutor的父类是BaseExecutor,BaseExecutor下一共有三个子类也就是三种执行器:BatchExecutor、SimpleExecutor、ReuseExecutor,这三种执行器均可通过传值设置。
cacheEnabled默认值为true,说明Mybatis默认会使用CachingExecutor。进入CachingExecutor类可以发现,CachingExecutor是在上面三种执行器(BaseExecutor)的基础上做了一层包装(装饰器设计模式),先调用CachingExecutor再调用BaseExecutor,是对BaseExecutor类的增强。
cacheEnabled可以通过mybatis-config.xml文件进行配置
<settings><!-- 是否开启二级缓存 --><setting name="cacheEnabled" value="false"/>
</settings>
BaseExecutor是一级缓存(默认开启),默认使用SimpleExecutor,CachingExecutor是二级缓存(默认开启,但还需要做一些额外的配置才能生效)
- 生成DefaultSqlSession
将Configuration、Executor、autoCommit等信息包装成DefaultSqlSession对象,并且返回该对象
总结
openSession()是SqlSessionFactory接口中的一个重载方法,可以配置执行器类型、事务隔离级别、是否自动提交等参数,Configuration负责判断当前使用的执行器(Executor),DefaultSqlSessionFactory最后将Configuration、Executor、autoCommit等信息包装成DefaultSqlSession对象并返回。
这里采用了装饰器设计模式
BaseExecutor是一级缓存(默认开启),BaseExecutor是BatchExecutor、SimpleExecutor、ReuseExecutor三种执行器的父类。
- SimpleExecutor:默认的Executor,每个SQL执行的时候都会创建新的Statement;
- ReuseExecutor:相同的SQL会重复使用Statement;
- BatchExecutor:用于批处理的Executor;
CachingExecutor是二级缓存(默认开启,但还需要做一些额外的配置才能生效)
CachingExecutor:可缓存数据的Executor,用装饰器模式包装了其它的执行器(如BaseExecutor下的三种执行器)
4. 获取mapper代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
DefaultSqlSession.java
Configuration.java
MapperRegistry.java
之前已经对mapper接口进行了注册,这里通过mapper接口类型获取对应的动态代理工厂类(MapperProxyFactory),动态代理工厂类使用JDK动态代理技术生成mapper代理对象并返回该对象。
MapperProxyFactory.java
MapperProxy.java
JDK动态代理技术主要用于拦截和修改方法的调用,在使用mapper代理对象调用mapper接口中的方法时MapperProxy中的invoke方法也会被执行。
总结
根据mapper接口类型从MapperRegistry中获取对应的动态代理工厂类(MapperProxyFactory),动态代理工厂类使用JDK动态代理技术生成mapper代理对象并返回该对象。在使用mapper代理对象调用方法时底层会走MapperProxy中的invoke方法。
这里采用了JDK动态代理设计模式
MapperRegistry:mapper接口动态代理工厂类的注册中心;
MapperProxyFactory:用于生成动态代理的实例对象;
MapperProxy:动态代理回调类;
5. 使用mapper代理对象执行Sql语句
List<UserEntity> list = mapper.listUser();
MapperProxy.java
核心代码
mapperMethod.execute(sqlSession, args);
MapperMethod.java
因为执行的SQL为select,返回值类型为List集合,所以会走executeForMany()方法
DefaultSqlSession.java
这个方法是不是很熟悉,没错,这就是在基于XML方式-原生方式开发用到的方法
List<UserEntity> list = sqlSession.selectList("com.mybatis.mapper.UserMapper.listUser", UserEntity.class);
DefaultSqlSession.java
二级缓存
如果开启了二级缓存则会使用CachingExecutor
CachingExecutor.java
- 获取SQL语句
- 创建缓存key
- 执行查询逻辑
SimpleExecutor中没有query方法,默认走父类(BaseExecutor)
一级缓存
BaseExecutor.java
SimpleExecutor.java
- 初始化RoutingStatementHandler对象
Configuration.java
RoutingStatementHandler.java
- 生成Statement对象
SimpleExecutor.java
2.1. 获取Connection
BaseExecutor.java
JdbcTransaction.java
2.2. 根据不同的StatementHandler创建Statement对象
RoutingStatementHandler.java
BaseStatementHandler.java
Mybatis默认采用PreparedStatementHandler处理器
PreparedStatementHandler.java
2.3. 使用ParameterHandler处理占位符参数
RoutingStatementHandler.java
PreparedStatementHandler.java
DefaultParameterHandler.java
- 执行查询逻辑
RoutingStatementHandler.java
PreparedStatementHandler.java
DefaultResultSetHandler.java
总结
在使用代理对象调用方法时,底层会走MapperProxy中的invoke方法,在执行查询语句时,默认会先从二级缓存(CachingExecutor)中读取数据,如果存在则直接返回,不存在则继续查询一级缓存,如果一级缓存(BaseExecutor)中存在则直接返回,不存在则继续查询数据库,在查询数据库时,总体上使用StatementHandler对象和JDBC进行交互,整个查询流程先是使用ParameterHandler对SQL语句的入参进行处理,待SQL语句被执行完后得到结果集,再使用ResultSetHandler对结果集进行处理并返回。
四大核心接口对象
- Executor(执行器):负责整个SQL执行过程的总体控制;
- StatementHandler(语句处理器):负责和JDBC层的具体交互;
- ParameterHandler(参数处理器):负责PreparedStatement入参的具体设置;
- ResultSetHandler(结果集处理器):负责将JDBC查询的结果映射为Java对象;
StatementHandler
- RoutingStatementHandler:根据StatementType路由到不同的StatementHandler对象;
- SimpleStatementHandler:管理Statement对象并向数据库中推送不需要预编译的SQL语句;
- PreparedStatementHandler:管理Statement对象并向数据库中推送需要预编译的SQL语句;
- CallableStatementHandler:管理Statement对象并调用数据库中的存储过程;