Mr. Cappuccino的第53杯咖啡——Mybatis源码分析

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

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第1张图片


总结

从入口一路点击进去可以发现底层是通过调用java.lang.ClassLoader#getResourceAsStream方法来读取resources目录下的mybatis-config.xml文件,并得到InputStream对象

2. 解析配置文件

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

SqlSessionFactoryBuilder.java

在这里插入图片描述

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第2张图片


核心代码(一)

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

XMLConfigBuilder.java

在这里插入图片描述

XPathParser.java

在这里插入图片描述

可以发现底层是将InputStream对象转换成Document对象,并将Document对象保存至当前类(XPathParser)的document属性中

继续回到上一层,点击进入this()方法

XMLConfigBuilder.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第3张图片

可以发现this()方法主要是在进行部分属性的初始化,并将XPathParser对象保存至当前类(XMLConfigBuilder)的parser属性中。
关键点:初始化了父类的configuration属性。


核心代码(二)

return build(parser.parse());

分析parse()方法


XMLConfigBuilder.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第4张图片

  1. 根据Document对象获取节点为configuration的配置信息,并转换成XNode对象

XPathParser.java

在这里插入图片描述

  1. 将XNode对象解析成Configuration对象

XMLConfigBuilder.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第5张图片

XMLConfigBuilder.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第6张图片


点击进入addMappers(mapperPackage)方法


Configuration.java

在这里插入图片描述

MapperRegistry.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第7张图片
Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第8张图片
Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第9张图片


从这里可以发现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对象核心属性释义:

  1. MapperRegistry:mapper接口动态代理工厂类的注册中心;
  2. ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id、result等子元素;
  3. MappedStatement:用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性;
  4. SqlSource:用于创建BoundSql,mapper.xml文件中的sql语句会被解析成BoundSql对象,经过解析BoundSql包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;

3. 获取SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();

DefaultSqlSessionFactory.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第10张图片

Configuration.java

在这里插入图片描述
Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第11张图片

从这里可以看到如果没有设置执行器类型,则会默认使用简单执行器类型

ExecutorType.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第12张图片

上面枚举类中的三种执行器类型均可通过openSession()传参设置


点击进入openSessionFromDataSource()方法


DefaultSqlSessionFactory.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第13张图片

openSessionFromDataSource()方法有三个入参:ExecutorType execType(执行器类型)、TransactionIsolationLevel level(事务隔离级别)、boolean autoCommit(是否自动提交)

  1. 获取TransactionFactory

DefaultSqlSessionFactory.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第14张图片

TransactionFactory有两种:JdbcTransactionFactory,ManagedTransactionFactory
通过mybatis-config.xml文件进行配置

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第15张图片

<transactionManager type="JDBC"/>

这里配置的是JdbcTransactionFactory

  1. 获取Transaction

JdbcTransactionFactory.java

在这里插入图片描述

JdbcTransaction.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第16张图片

  1. 根据Transaction和执行器类型获取执行器(核心代码)

Configuration.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第17张图片

CachingExecutor.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第18张图片

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是二级缓存(默认开启,但还需要做一些额外的配置才能生效)

  1. 生成DefaultSqlSession

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第19张图片

将Configuration、Executor、autoCommit等信息包装成DefaultSqlSession对象,并且返回该对象


总结

openSession()是SqlSessionFactory接口中的一个重载方法,可以配置执行器类型、事务隔离级别、是否自动提交等参数,Configuration负责判断当前使用的执行器(Executor),DefaultSqlSessionFactory最后将Configuration、Executor、autoCommit等信息包装成DefaultSqlSession对象并返回。

这里采用了装饰器设计模式

BaseExecutor是一级缓存(默认开启),BaseExecutor是BatchExecutor、SimpleExecutor、ReuseExecutor三种执行器的父类。

  1. SimpleExecutor:默认的Executor,每个SQL执行的时候都会创建新的Statement;
  2. ReuseExecutor:相同的SQL会重复使用Statement;
  3. BatchExecutor:用于批处理的Executor;

CachingExecutor是二级缓存(默认开启,但还需要做一些额外的配置才能生效)
CachingExecutor:可缓存数据的Executor,用装饰器模式包装了其它的执行器(如BaseExecutor下的三种执行器)

4. 获取mapper代理对象

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

DefaultSqlSession.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第20张图片

Configuration.java

在这里插入图片描述

MapperRegistry.java

在这里插入图片描述

之前已经对mapper接口进行了注册,这里通过mapper接口类型获取对应的动态代理工厂类(MapperProxyFactory),动态代理工厂类使用JDK动态代理技术生成mapper代理对象并返回该对象。

MapperProxyFactory.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第21张图片

MapperProxy.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第22张图片

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();

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第23张图片


MapperProxy.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第24张图片

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第25张图片

核心代码

mapperMethod.execute(sqlSession, args);

MapperMethod.java

因为执行的SQL为select,返回值类型为List集合,所以会走executeForMany()方法

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第26张图片

DefaultSqlSession.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第27张图片

这个方法是不是很熟悉,没错,这就是在基于XML方式-原生方式开发用到的方法

List<UserEntity> list = sqlSession.selectList("com.mybatis.mapper.UserMapper.listUser", UserEntity.class);

DefaultSqlSession.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第28张图片

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第29张图片

二级缓存

如果开启了二级缓存则会使用CachingExecutor

CachingExecutor.java

在这里插入图片描述

  1. 获取SQL语句

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第30张图片

  1. 创建缓存key

在这里插入图片描述

  1. 执行查询逻辑

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第31张图片

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第32张图片

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第33张图片

SimpleExecutor中没有query方法,默认走父类(BaseExecutor)


一级缓存

BaseExecutor.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第34张图片
Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第35张图片

SimpleExecutor.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第36张图片

  1. 初始化RoutingStatementHandler对象

Configuration.java

在这里插入图片描述

RoutingStatementHandler.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第37张图片

  1. 生成Statement对象

SimpleExecutor.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第38张图片

2.1. 获取Connection

BaseExecutor.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第39张图片

JdbcTransaction.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第40张图片

2.2. 根据不同的StatementHandler创建Statement对象

RoutingStatementHandler.java

在这里插入图片描述

BaseStatementHandler.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第41张图片

Mybatis默认采用PreparedStatementHandler处理器

PreparedStatementHandler.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第42张图片

2.3. 使用ParameterHandler处理占位符参数

RoutingStatementHandler.java

在这里插入图片描述

PreparedStatementHandler.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第43张图片

DefaultParameterHandler.java

  1. 执行查询逻辑

RoutingStatementHandler.java

在这里插入图片描述

PreparedStatementHandler.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第44张图片

DefaultResultSetHandler.java

Mr. Cappuccino的第53杯咖啡——Mybatis源码分析_第45张图片


总结

在使用代理对象调用方法时,底层会走MapperProxy中的invoke方法,在执行查询语句时,默认会先从二级缓存(CachingExecutor)中读取数据,如果存在则直接返回,不存在则继续查询一级缓存,如果一级缓存(BaseExecutor)中存在则直接返回,不存在则继续查询数据库,在查询数据库时,总体上使用StatementHandler对象和JDBC进行交互,整个查询流程先是使用ParameterHandler对SQL语句的入参进行处理,待SQL语句被执行完后得到结果集,再使用ResultSetHandler对结果集进行处理并返回。

四大核心接口对象

  1. Executor(执行器):负责整个SQL执行过程的总体控制;
  2. StatementHandler(语句处理器):负责和JDBC层的具体交互;
  3. ParameterHandler(参数处理器):负责PreparedStatement入参的具体设置;
  4. ResultSetHandler(结果集处理器):负责将JDBC查询的结果映射为Java对象;

StatementHandler

  1. RoutingStatementHandler:根据StatementType路由到不同的StatementHandler对象;
  2. SimpleStatementHandler:管理Statement对象并向数据库中推送不需要预编译的SQL语句;
  3. PreparedStatementHandler:管理Statement对象并向数据库中推送需要预编译的SQL语句;
  4. CallableStatementHandler:管理Statement对象并调用数据库中的存储过程;

你可能感兴趣的:(mybatis,mr,mybatis,tomcat)