MyBatis源码阅读

编程式开发使用MyBatis

在研究MyBatis源码之前,先来看下单独使用MyBatis来查询数据库时是怎么做的:

1 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
2 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
3 SqlSession sqlSession = sqlSessionFactory.openSession();
4 BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
5 Blog blog = blogMapper.selectByBlogId("1");
6 System.out.println(blog.toString());

第一步:把全局配置文件读取成流;

第二步:SqlSessionFactoryBuilder根据配置文件构建SqlSessionFactory;

第三步:SqlSessionFactory通过调用openSession()方法创建会话SqlSession;

第四步:SqlSession调用getMapper方法获取Mapper接口对象;

第五步:Mapper接口对象执行查询。

此处,我们把文件读取成流的步骤就省略了,直接从第二步构建SqlSessionFactory工厂开始解析源码。

1、构建SqlSessionFactory工厂

MyBatis通过建造者模式创建一个工厂,配置文件的解析就是在这一步完成的,包括mybatis-config.xml和Mapper.xml文件。

(1)配置解析过程

从上面的代码中可以看到:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

首先new了一个SqlSessionFactoryBuilder,非常明显的建造者模式,它里面定义了很多个build方法的重载,最终返回的是一个SqlSessionFactory对象(单例模式)。

我们看下build方法源码:

 1 public SqlSessionFactory build(InputStream inputStream) {
 2     return build(inputStream, null, null);
 3 }
 4
 5 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
 6     try {
 7       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
 8       return build(parser.parse());
 9     } catch (Exception e) {
10       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
11     } finally {
12       ErrorContext.instance().reset();
13       try {
14         inputStream.close();
15       } catch (IOException e) {
16         // Intentionally ignore. Prefer previous error.
17       }
18     }
19 }

这里面创建了一个XMLConfigBuilder对象(Configuration对象也是这个时候创建的)。

XMLConfigBuilder是抽象类BaseBuilder的一个子类,专门用来解析全局配置文件,针对不同的构建目标还有其他的一些子类,比如:XMLMapperBuilder用来解析Mapper映射器,XMLStatementBuilder用来解析增删改查标签。

XMLConfigBuilder先执行parse方法解析xml文件,返回一个Configuration对象:

1 public Configuration parse() {
2     if (parsed) {
3       throw new BuilderException("Each XMLConfigBuilder can only be used once.");
4     }
5     parsed = true;
6     parseConfiguration(parser.evalNode("/configuration"));
7     return configuration;
8 }

配置文件里面所有的信息都会放在Configuration里面。Configuration类里面有很多属性,有很多是跟config里面的标签直接对应的。

然后执行SqlSessionFactoryBuilder建造者的build方法,把根据配置文件生成的Configuration对象传进去:

1 public SqlSessionFactory build(Configuration config) {
2     return new DefaultSqlSessionFactory(config);
3 }

可以看到,直接返回一个默认工厂DefaultSqlSessionFactory对象。

我们重点关注解析配置文件到Configuration对象中的parse方法:

看源码知道,parse方法会先检查是不是已经解析过,也就是说在应用的生命周期里面,config配置文件只需要解析一次,生成的Configuration对象也会存在应用的整个声明周期。接下来就是parseConfiguration方法:

 1 private void parseConfiguration(XNode root) {
 2     try {
 3       //issue #117 read properties first
 4       propertiesElement(root.evalNode("properties"));
 5       Properties settings = settingsAsProperties(root.evalNode("settings"));
 6       loadCustomVfs(settings);
 7       loadCustomLogImpl(settings);
 8       typeAliasesElement(root.evalNode("typeAliases"));
 9       pluginElement(root.evalNode("plugins"));
10       objectFactoryElement(root.evalNode("objectFactory"));
11       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
12       reflectorFactoryElement(root.evalNode("reflectorFactory"));
13       settingsElement(settings);
14       // read it after objectFactory and objectWrapperFactory issue #631
15       environmentsElement(root.evalNode("environments"));
16       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
17       typeHandlerElement(root.evalNode("typeHandlers"));
18       mapperElement(root.evalNode("mappers"));
19     } catch (Exception e) {
20       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
21     }
22 }

这里面有十几个方法,对应着config文件里面的所有一级标签。下面对这些方法做一下简要说明:

propertiesElement()

解析properties标签,读取我们引入的外部配置文件。这里有两种类型,一种是放在resource目录下的,是相对路径,一种是写的绝对路径。解析的最终结果就是我们会把所有的配置信息放到名为defaults的Properties对象里面,最后把XPathParser和Configuration的Properties属性都设置成我们填充后的Properties对象。

settingsAsProperties()

把settings标签也解析成了Properties对象,对于<settings>的子标签的处理在后面。之所以这里先将settings标签解析成Properties对象,是因为下面两个方法要用。

loadCustomVfs(settings)

loadCustomVfs是获取Vitual File System的自定义实现类,比如我们要读取本地文件,或者FTP远程文件的时候,就可以用到自定义的VFS类。我们根据<settings>标签里面的<vfsImpl>标签,生成一个抽象类VFS的子类,并且赋值到Configuration中。

loadCustomLogImpl(settings)

loadCustomLogImpl是根据<logImpl>标签获取日志的实现类,我们可以用到很多的日志的方案,包括log4j、log4j2、slf4j等,这里生成一个Log接口的实现类,并且赋值到Configuration中。

typeAliasesElement()

接下来,解析<typeAliases>标签,它有两种定义方式,一种是直接定义一个类的别名,一种就是指定一个包,那么这个package下面所有的类的名字就会成为这个类全路径的别名。类的别名和类的关系,放在一个TypeAliasRegistry对象里面。

pluginElement()

接下来解析的是<plugins>标签,比如pageHelper的翻页插件,或者我们自定义的插件。<plugins>标签里面只有<plugin>标签,<plugin>标签里面只有<property>标签。标签解析完以后,会生成一个Interceptor对象,并且添加到Configuration的InterceptorChain属性里面,它是一个List。

objectFactoryElement()、objectWrapperFactoryElement()

这两个标签是用来实例化对象用的,<objectFactory>和<objectWrapperFactory>这两个标签,分别生成ObjectFactory、ObjectWrapperFactory对象,同样设置到Configuration属性里面。

reflectorFactory()

解析<reflectorFactory>标签,生成ReflectorFactory对象。

settingsElement(settings)

这里就是对<settings>标签里面所有子标签的处理了,前面已经把子标签全部转换成了Properties对象,所以在这里处理Properties对象就可以了。二级标签里面有很多的配置,比如二级缓存,延迟加载,自动生成主键这些。需要注意的是,这些标签的所有的默认值都是在这里赋值的。所有的值都会赋值到Configuration对象里去。

environmentsElement()

这一步解析<environments>标签。我们知道,一个environment对应一个数据源,所以在这里我们会根据配置的<transactionManager>创建一个事务工厂,根据<dataSource>标签创建一个数据源,最后把这两个对象设置成Environment对象的属性,放到Configuration里面。

databaseIdProviderElement()

解析<databaseIdProvider>标签,生成DatabaseIdProvider对象(用来支持不同厂商的数据库)。

typeHandlerElement()

跟typeAlias一样,TypeHandler有两种配置方式,一种是单独配置一个类,一种是指定一个package。最后我们得到的是JavaType和JdbcType,以及用来做相互映射的TypeHandler之间的映射关系。最后放在TypeHandlerRegistry对象里面。

问题:这种三个对象(Java类型,JDBC类型,Handler)的关系怎么映射?(Map里面再放一个Map)

mapperElement()

http://www.mybatis.org/mybatis-3/zh/configuration.html#mappers

(1)判断

最后就是Mapper标签的解析,<mapper>标签的扫描类型:

扫描类型 含义
resource 相对路径
url 绝对路径
class 单个Mapper接口
package

(2)注册

XMLMapperBuilder.parse()方法是对Mapper映射器的解析。里面有两个方法:

configurationElement()--解析所有的子标签,其中buildStatementFromContext()最终获得MappedStatement对象。

bindMapperForNamespace--把namespace(接口类型)和工厂类绑定起来。

2、创建SqlSession会话

3、获取Mapper接口

4、调用接口方法

原文地址:https://www.cnblogs.com/yybinger/p/11959426.html

时间: 2024-08-02 20:52:39

MyBatis源码阅读的相关文章

Mybatis源码阅读之--整体执行流程

Mybatis执行流程分析 Mybatis执行SQL语句可以使用两种方式: 使用SqlSession执行update/delete/insert/select操作 使用SqlSession获得对应的Mapper,然后调用mapper的相应方法执行语句 其中第二种方式获取Mapper的流程在前面已经解析过,请查看文章Mybatis源码阅读之--Mapper执行流程 其实这个方法最后的MapperMthod也是调用SqlSession的相应方法执行增删该的操作,这边文章主要介绍SqlSession执

mybatis源码阅读-高级用法(二)

新建学生表和学生证表 --学生表 CREATE TABLE student( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'id', `name` VARCHAR(20) NOT NULL COMMENT '姓名', `age` INT NOT NULL COMMENT '年龄', sex INT NOT NULL COMMENT '性别 1 男 0 女', cid INT NOT NULL COMMENT '班级id', cardId

mybatis源码阅读-SqlSessionFactory(三)

我们的一个mybatis程序 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //返回的DefaultSqlSessionFactory的实例 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder .build(ClassLoader.getSystemResourceAsStream("mybatis.x

mybatis源码阅读 (五)

mybatis中的缓存,有一个疑问为什么一级缓存需要先放一个占位值,查询到结果后再移除,放入真正的值???代码标红处 1.二级缓存 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(p

Mybatis源码阅读之--本地(一级)缓存实现原理分析

前言: Mybatis为了提升性能,内置了本地缓存(也可以称之为一级缓存),在mybatis-config.xml中可以设置localCacheScope中可以配置本地缓存的作用域,包含两个值session和statement,其中session选项表示本地缓存在整个session都有效,而statement只能在一条语句中有效(这条语句有嵌套查询--nested query/select). 下面分析一下mybatis本地缓存的实现原理. 本地缓存是在Executor内部构建,Executor

mybatis源码阅读(三)

SqlSesion怎么获取一个Mapper? 一个Mapper接口没有一个实现类怎么能够实例化? public <T> T getMapper(Class<T> type) { // 通过 configuration 的getMapper方法获取Mapper对象 return configuration.<T>getMapper(type, this); } public <T> T getMapper(Class<T> type, SqlSes

mybatis源码阅读(二)

通过SqlSessionFactory 创建 SqlSession // 通过SqlSessionFactory 获取创建一个SqlsessionSqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), nu

手把手带你阅读Mybatis源码(三)缓存篇

前言 大家好,这一篇文章是MyBatis系列的最后一篇文章,前面两篇文章:手把手带你阅读Mybatis源码(一)构造篇 和 手把手带你阅读Mybatis源码(二)执行篇,主要说明了MyBatis是如何将我们的xml配置文件构建为其内部的Configuration对象和MappedStatement对象的,然后在第二篇我们说了构建完成后MyBatis是如何一步一步地执行我们的SQL语句并且对结果集进行封装的. 那么这篇作为MyBatis系列的最后一篇,自然是要来聊聊MyBatis中的一个不可忽视的

MyBatis源码解读(1)——SqlSessionFactory

在前面对MyBatis稍微有点了解过后,现在来对MyBatis的源码试着解读一下,并不是解析,暂时定为解读.所有对MyBatis解读均是基于MyBatis-3.4.1,官网中文文档:http://www.mybatis.org/mybatis-3/zh/getting-started.html,MyBatis-3.4.1.jar. 本应在开始读MyBatis源码时首先应该了解下MyBatis的SqlSession的四大对象:Executor.StatemenHandler.ParameterHa