# 分层架构图

# 用户基本操作

分析基本操作,后面深入了解<mark>每步操作的源码流程</mark>

  1. 获取 sqlSessionFactory 对象

  2. 获取 sqlSession 对象

  3. 获取接口***对象(MapperProxy)

  4. 执行增删改查方法


# 了解源码 - 目的

<mark>为 mybatis 插件开发做铺垫</mark>


源码学习过程中

着重注意以下几个类

  • Executor - <mark>执行器</mark>
  • ParameterHandler - <mark>参数处理器</mark>
  • ResultSetHandler - <mark>结果集处理器</mark>
  • StatementHandler - <mark>SQL语句处理器</mark>

# 源码 - 1: 看 builder ⇒ factory 全流程

在 我们 自己写的 getSqlSessionFactory 上打一个断点

build 一进来, 创建 XMLConfigBuilder 用来解析 xml 配置文件


进入到 parse 方法

看到 调用 parseConfiguration ,用来解析配置文件

看看 parseConfiguration 怎么玩的

可见(上图),先拿到 configuration 节点(根节点,对应xml文件就能清楚看到)

然后 挨个解析每一个<mark>节点信息</mark>

  • settings
  • properties
  • typeAliases
  • plugins
  • ...

然后,再进入 看 settingsElement

每一个 settings 标签 能进行的设置,都能看到了(下图) 🐶🐶🐶

❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️

🌻🌻🌻🌻🌻🌻🌻🌟🌟🌟🌟🌟🌟🌟🌟🌟

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️


同样的 ,看看 mapperElement

各种判断,看看 <mapper> 注解上,我们用了什么方式 引用的 mapper.xml 文件


分析完成后,(绿框) , 调用相应的解析方法

这里的 parse 是什么? XPathParser


点进去 看 parse() 方法 , <mark>看怎么解析 mapper 文件的</mark>


(下图)先拿到 namespace



然后 分析:

  • parameterMap
  • resultMap
  • sql
  • select / insert / update / delete

进入 解析 增删改查方法


可以看到,(结合上下面),context 就是我们 的每一条 包含 sql 语句的标签。

statementParser 就是对这每一条的标签的 解析类


这个类 调用了 parseStatementNode 方法,点进去看

🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻
😻😻😻😻😻😻😻😻😻
🌹🌹

(下面) 对 select / insert / delete / update 标签的 各种属性 的解析

🌹🌹
😻😻😻😻😻😻😻😻😻😻
🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻🍻

拿出 这些标签的 详细属性 干什么?

在方法最后,看到 ,

用这些属性 构造了 一个 MappedStatement ( 下图 )

自然的 , 我们呢 进去 看 addMapStatement


最终返回的 是一个 封装好的 statement

🍁🍁🍁🍁🍁🍁🍁🍁🍁
🍃🍃🍃🍃🍃🍃

不难总结出:mybatis配置里面,<mark>一个MappedStatement就代表一个增删改查标签的详细信息</mark>
🌴🌴🌴🌴🌴🌴🌴🌴

好,经过这样 的 各种循环之后,配置的解析就ok了。

回到 XMLConfigBuilder的 parse 方法,看返回 的 configuration

里面包含 了 所有


。。。
接上面

。。。

这里 还有 一个重要的 属性

mapperRegistry
(下图)可以看到,这个属性记录的 每个 mapper 都有一个对应的 ***工厂

再 return 会 builder

builder 用 这个 configuration 最终创建 工厂实例

## 总结

根据配置,创建SQLSessionFactory

(把配置文件的信息解析并保存在 Configuration 对象中,返回 DefaultSqlSession 对象)

# 源码 - 2 :factory ⇒ session 全流程

上面 看到了 SqlSessionFactory 的获取过程

下面 看 openSession , 看 factory 获取 session 的全过程

这里,sqlsessionFactory 的 实现类 是 DefaultSqlSessionFactory

opensession() 方法里面 调用的 是 openSessionFromDataSource ,

这里 传进去的 第一个参数 是 configuration.getDefaultExecutorType() (执行器的类型)

值是 SIMPLE , 什么意思?

官方文档有讲,
值有三
+ SIMPLE (默认) ==》 <mark>简单的执行器</mark>
+ RESUSE - 重用 ==》 <mark>可以重用的执行器</mark>
+ BATCH - 批量 ==》 <mark>做批量操作的执行器</mark>

(下图)可见,事务在这里开启

关键,看 Executor

点进 Executor 接口,定义了 增删查改 、事务、缓存 三部分的 方法

进入 newExecutor 方法

这里(下图),就会根据之前说的 type 不同,创建不同的 Executor

然后 判断是否开启二级缓存。

如果开启, 创建 CachingExecutor

点进去看 CachingExecutor

(下图) , 构造方法,对传入的 Executor 进行包装

实际上,还是 传入的 Executor 执行 Execute 操作

这样做的作用?
就 query 查询为例,用 CacheExecutor 的 query 会 在 查询前 查 缓存,如果有,直接返回 查询结果。

下面看 interceptorChain.pluginAll

***链的 插入 全部 插件 方法


可以看到(下图),就是遍历所有的 ***(interceptor) , 对executor 进行包装


newExecutor 方法看完。

退回到 外面

(下图)创建 DefaultSqlSession ,里面包含了 configuration 和 executor 的信息

<mark>最终,返回 DefaultSqlSession</mark>

## 总结

获取 SqlSession 对象
返回一个 DefaultSqlSession 对象,包含 Executor 和 Configuration
这一步会创建 Executor

# 源码 - 3 :session ⇒ mapper 全流程

DefaulttSqlSession 的 getMapper 调用的是 configuration 的 getMapper


两个参数

type 是 mapper 接口 的类型,调用的 用户指定


this 是 调用 configuration 的 DefaultSqlSession

进入 configuration 的 getMapper 方法
可以看到(下图), configuration 调用的 是 mapperRegistry 的 getMapper 方法

雷丰阳

mapperRegistry 是什么?
configuration 的 一个属性

其实之前 也见过

讲 获取 SqlSessionFactoryBuilder 时候 见到过
<mark>registry 里面包含了 配置的 mapper 和 对应的 *** 工厂 MapperProxyFactory</mark>

进去 mapperRegistry 的 getMapper 看(下图)

<mark>首先,从 registry >> knownMappers 下 获取 ***工厂</mark>(根据接口类型)



(如果拿不到,就报错)
如果拿到了 ,就获取实例

进入 newInstance

可以看到 是对 一些 属性的封装

  • sqlSesson
  • mapperInterface - 当前的接口
  • methodCache - 当前方法对应的 缓存 map


点进去

⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️

<mark>这里 看到 MapperProxy 实现了 InvocationHandler !!!</mark>

⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️⭐️⭐️

InvocationHandler 是什么?
Proxy 的 newProxyInstance 方法


他这里说了
<mark>invocationHandler 是用来做method dispatch 的</mark>
也就是 ,指定***的增强 具体做什么

继续看 newInstance,(下图)
其实 , 就是调用了 java.lang.reflect.Proxy 的 newProxyInstance ,创建一个 ***对象。
(其中,传入 mapperProxy 指定 ***的具体操作)

最后,创建出来的 ***对象,被传出去 给用户使用

## 总结

获取接口的***对象(MapperProxy)
getMapper 使用 MapperProxyFactory 创建一个 MapperProxy 的***对象
***对象里面包含了, DefaultSqlSession (这里 包含了 执行增删改查的 Executor)

# 源码 - 4 : mapper ⇒ 查询



(下图)看是不是 jdk 默认的方法

如果不是 默认的方法(是用户指定的方法) , 就把 method 封装成 MapperMethod (mapper 能认识的 method 类)
然后 execute 执行方法

然后 进入 execute 方法

(下图) 判断 是

  • insert
  • update
  • delete
  • select

的哪一种

以 select 为例

根据返回值类型的不同 ,调用不同 方法 获取 result 值

如果 只返回一个 对象,就会来到最后的判断

首先 ,执行 method.convertArgsToSqlCommandParam 分析 参数 , 获得 param



(下图)如果参数时单个,返回单个,如果参数时多个,包装成一个 map 返回

返回到 execute 方法,下一步 执行 sqlSession 的 selectOne (下一步)


上面一直看下来,我们知道 sqlsession 的 实现类是 defaultSqlSession
看 defaultSqlSession 的 selectOne(下图)

(上图)可以看到,尽管是 selectone ,调用的 还是 selectList (查询多个)

看 selectList 是怎么查询的

(上图) 先从 configuration 里面 拿到 statement 的 映射集

<mark>里面映射了方法的详细信息</mark>


然后,把映射的 方法详细信息传递给 executor 的 query 查询方法

<mark>executor.query</mark> 方法 参数里面还调用一个方法
wrapCollection 分析传入参数 是 集合的情况
点进去 看看

再看 query

(上图)getBoundSql 获取绑定的 sql



然后 后面 算出 cache 的key

key: hashCode + 方法id + sql + 参数 xxx 等。。。

特别长 ↓


然后是 重写 的 query (下图)


判断有没有缓存,
有 就 缓存取值 直接返回

没有,就 调用 delegate 的 query 方法

(上图) executor 的 实例 这里是 SimpleExecutor (具体是什么,在前面说了,根据配置创建)

所以,这里,就点进 SimpleExecutor 的 query 方法

(我们发现,SimpleExecutor 继承 BaseExecutor , query 在 BaseExecutor 中 就已经写好了)

可以看到,这里是会 尝试 从一级缓冲(localCache)中取数据



(上图)但是,因为是第一次查,所以 没有数据

我们进入 queryFromDatabase

首先,在缓存中 在 命名空间中 先 放一个占位符(下面)

再 查到数据 (list) 之后,再放list入缓存




然后是 , doQuery ,下面 看看 传入的 参数

ms mappedStatement 这次查询的 详细 标签信息

参数

rowBounds 做逻辑分页(不分页 下面就默认值)

resultHandler 结果处理

boundSQL 就是 sql 语句

点进去 doQuery


<mark>statement 是 原生jdk 的 statement哦</mark>

(下图)拿到配置信息

<mark>下面 是 new statementHandler 对象(四大 对象之一哦)</mark>

作用?
可以创建出 statement 对象。


(下图) 首先 new 一个 RoutingStatementHandler

看 RoutingStatementHandler 的构造 (下图)

判断 statementType , 创建响应的 statementHandler


StatementType 是在哪里定义的?
mapper.xml 里面

默认值? Prepared

所以 这里 new 的是 默认的 PrepareStatementHandler (下图)

调用的 PreparedStatementHandler 父类 BaseStatementHandler (下图)

BaseStatementHandler 的构造 对传入 参数 进行封装 (下图)

参数:

  • executor - 执行器
  • mappedStatement - 详细信息(最强)
  • parameterObject - 参数
  • rowBounds - 分页信息(这里没用)
  • resultHandler - 结果处理器
  • boundSql - sql 语句


(上图) 需要留意,这里new 了两个 处理器
一个 是 参数的
一个是 结果的

注意: 两个末尾都调用了 pluginAll



(下图) 然后 , 调用 interceptorChain 的 所以插件 方法 pluginAll (这和 前面的 executor 一样)

然后 继续返回

然后用 prepareStatement 创建 jdk 原生的 statement 对象 (下图)


创建 原生 statement 的过程也很简单(下图)

(下图) 打开 连接

通过 handler 的 prepare 方法 创建 statement


(下图) handler.parameterize 参数预编译赋值

预编译 sql 产生 PreparedStatement 对象(下图)


RoutingStatementHandler干嘛的??


RoutingStatementHandler 类似路由器,
在<mark>其构造函数中会根据Mapper文件中设置的StatementType来选择</mark>
使用
SimpleStatementHandler、
PreparedStatementHandler
或是CallableStatementHandler

其实现的接口StatementHandler的方法也是由这三个具体实现类来实现。
《Mybatis源码之Statement处理器RoutingStatementHandler(三)》

到 RoutingStatementHandler 的 parameterize 方法(下面)

Routing 路由

注意:这里的 delegate 是 preparedStatementHandler

在之前的代码 看到 ,选择了 preparedStatement

再 进去 parameterize

这里的 parameterHandler 是在 构造 BaseStatementHandler 时候 赋值的

然后,setParameters (这里是 DefaultParameterHandler)

setParameters 干嘛?


之前不是预编译了嘛。
这里就把 预编译时候的 “?” 填上值


首先,是获取 参数映射

对参数值 做获取(下图)

typeHandler 根据 类型 对 参数值 和参数名做一一映射(下图)



类型解析处理器(下图)

这里,(我们的po属性就是 long) (下图)


这里,就有专门的 long 类型的处理器(下图)

设置好了,就一直返回

(这里 logger 是因为 我开了 aop 和 日志)

继续看 handler 的 query 方法(下图)
(前面三个代码1、准备配置,2、准备处理器,3、准备statement 和预编译。现在是真的查了!!!)
点进去

preparedStatement 执行 execute 方法(这里 是 java.sql.PreparedStatement

(完)

另外,还可以看看 execute 的调用


## 总结

总结的流程如下:

  1. 根据配置文件(全局,sql映射)初始化出 Configuration 对象
  2. 创建一个 DefaultSqlSession 对象
    他里面包含 Configuration
    以及 Executor (根据 全局 配置文件中的 defaultExecutorType 创建出对应的 Executor )
  3. DefaultSqlSession.getMapper() 拿到 Mapper 接口对应的 MapperProxy
  4. MapperProxy 里面 有 (DefaultSqlSession)
  5. 执行增删改查方法
    1. 调用 DefaultSqlSession 的增删改查 (Executor)
    2. ·会创建一个 StatementHandler 对象。
      (同时也会创建 ParameterHandlerResultSetHandler
    3. 调用 StatementHandler 的预编译策略和 参数设置值 (使用 ParameterHandler
    4. 调用 StatementHandler 的 增删改查
    5. ResultSetHandler 封装 结果

即:

***对象 =》 DefaultSqlSession =》 Executor =》 StatementHandler