Mybatis3源码分析(4)插件分析

天涯浪子 提交于 2020-02-20 03:04:25

源码总结回顾

对象 相关对象 作用
Configuration MapperRegistry TypeAliasRegistry TypeHandlerRegistry 包含了 MyBatis 的所有的配置信息
SqlSession SqlSessionFactory DefaultSqlSession 对操作数据库的增删改查的 API 进行了封装,提供给应用层使用
Executor BaseExecutor SimpleExecutor BatchExecutor ReuseExecutor MyBatis 执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成和查 询缓存的维护
StatementHandler BaseStatementHandler SimpleStatementHandler PreparedStatementHandler CallableStatementHandler 封装了 JDBC Statement 操作,负责对 JDBC statement 的操作,如设 置参数、将 Statement 结果集转换成 List 集合
ParameterHandler DefaultParameterHandler 把用户传递的参数转换成 JDBC Statement 所需要的参数
ResultSetHandler DefaultResultSetHandler 把 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合
MapperProxy MapperProxyFactory 代理对象,用于代理 Mapper 接口方法
MappedStatement SqlSource BoundSql MappedStatement 维护了一条select|update|delete|insert节点 的封装,包括了 SQL 信息、入参信息、出参信息

插件拦截的四大对象

对象 描述 可拦截的方法 方法作用
Executor 上层的对象,SQL 执行全过程,包括组装参数,组装结果集返回和执行 SQL 过程 update 执行 update、insert、delete 操作
query 执行 query 操作
flushStatements 在 commit 的时候自动调用,SimpleExecutor ReuseExecutor、BatchExecutor 处理不同
commit 提交事务
rollback 事务回滚
getTransaction 获取事务
close 结束(关闭)事务
isClosed 判断事务是否关闭
StatementHandler 上层的对象,SQL 执行全过程,包括组装参数,组装结果集返回和执行 SQL 过程 prepare (BaseSatementHandler)SQL 预编译
parameterize 设置参数
batch 批处理
update 增删改操作
query 查询操作
ParameterHandler SQL 参数组装的过程 getParameterObject 获取参数
setParameters 设置参数
ResultSetHandler 结果的组装 handleResultSets 处理结果集
handleOutputParameters 处理存储过程出参

代理和拦截是怎么实现的?

我们从之前的课程知道插件是通过拦截时通过jdk动态代理的。

四大对象什么时候被代理,也就是:代理对象是什么时候创建的?

我们还记得Executor上个文章里面讲到,实在openSession时创建的,StatementHandler是SimpleExecutor.doQuery()里面创建的,里面包含了处理参数的 ParameterHandler 和处理结果集的 ResultSetHandler 的创建,创建之后即调用InterceptorChain.pluginAll(),返回层层代理后的对象。

多个插件的情况下,代理能不能被代理?代理顺序和调用顺序的关系?

谁来创建代理对象?

我们可以看从pagehelper这个插件进入看看-首先我们先从pageHelper的注册类进去

可以看到实现了interceptor这个类----这样就形成了代理类
下面这个方法就是mybatis的操作创建插件的方法:这个之后会分析

被代理后,调用的是什么方法?怎么调用到原被代理对象的方法?

我们可以看到在intercept方法里面拿到执行器是调用的gettarget这个方法,利用代理类调用的,我们进入invocation看看
可以看到可以通过target方法拿到被代理对象

因为代理类是 Plugin,所以最后调用的是 Plugin 的 invoke()方法。它先调用了定义的拦截器的 intercept()方法。可以通过 invocation.proceed()调用到被代理对象被拦截的方法。

再来看看他是怎么对我们的天王代理的

通过openSession 进去找到创建executor的时候 他调用了我们放在Configuration里面的interceptorChain

可以看到调用了所有插件-拿到所有实现了Interceptor的拦截器,然后这里就调用了实现Interceptor的类的plugin这个方法进行创建代理插件代理对象。这样被代理后我们之后的操作都会走到代理类的invoke方法里面。


这样就会调用我们自定义的拦截器的intercept方法–之后里面就是我们自定已的逻辑操作了。

实现Interceptor的三个方法含义

intercept():做我们想要做的事情 可以通过invocation 这个参数获取被拦截对象方法参数或者执行代理方法 --调用proceed可以走到被拦截对象的被拦截方法,这样就走完整个流程了。
plugin(): 创建代理对象,通过代理对象的invoke方法走到我们的插件
setProperties():这是我们在配置我们插件时可以提供很多属性,比如分页插件设置一些参数等等。

pageHelper插件案例分析


他是怎么来改写我们的sql的呢–我们首先来看看PageInterceptor里面的自定义逻辑

根据方言进入不同的实现类


然后我们在看看分页参数哪里来的

可以看到这里startPage调用时传进来的,然后通过本地线程里面拿到的(每个线程里面拿到的都是属于自己独有的分页数据)

这也就是写了startPage那句话,就会修改我们的sql 分页–这也就出现一个问题,在一个线程里我们的查询分页就有不稳定性!

对象 作用
PageInterceptor 自定义拦截器
Page 包装分页参数
PageInfo 包装结果
PageHelper 工具类

mybatis插件到底可以干些什么

  1. 动态改变数据源。水平分表,通过注解的方式:可以通过invocation拿到方法的注解,在接口上添加注解,通过反射获取接口注解,根据注解上配置的参数进行分表,修改原 SQL,例如 id 取模,按月分表
  2. 记录日志。
  3. 数据权限—对不同用户查询出来的数据有些筛选
  4. 脱敏也是可以的,数据加解密
  5. 对数据查询的执行分析,比如执行时长的分析。

插件调用的流程图

上一个简单的sql执行时长分析插件

type: 表示拦截的类,这里是StatementHandler的实现类
method:表示拦截的方法,这里是拦截StatementHandler的query方法
args:表示方法参数(可以通过数组参数接收然后强转)

看看pagehelper的

SpirngBoot注册插件

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!