Mybatis 插件原理解析
SqlSessionFactory 是 MyBatis 核心类之一,其重要功能是创建 MyBatis 的核心接口 SqlSession。MyBatis 通过 SqlSessionFactoryBuilder 构建 SqlSessionFactory,构建分为两步:
这种创建方式是一种 Builder 模式。对于复杂对象而言,直接使用构造函数构建会导致大量的逻辑放在构造函数中,使得代码看起来很繁杂。使用一个参数类总领全局,然后按步骤构建可以降低构建的复杂性
Configuration 的作用如下:
插件需要频繁访问映射器内部组成,所以有必要单独研究一下映射器的内部组成。一般而言,映射器由 3 部分组成:
image
image
一般而言,我们主要对参数和 SQL 进行修改,这部分主要反映在 BoundSql 类上,在插件中可以通过 BoundSql 拿到当前运行 SQL 和参数及参数规则,并做出适当修改,满足我们的需求
BoundSql 主要有三个属性:sql、rameterObject 和 parameterMappings
有了 Configuration 便可以快速构建 SqlSessionFactory
todo
映射器其实就是一个动态代理对象,最终会进入 MapperMethod 的 execute 方法。execute 经过简单判断就调用 SqlSession 的删除、更新、插入、选择等方法。这些方法是通过 Executor、StatementHandler、ParameterHandler 和 ResultHandler 来完成数据库操作和结果返回的
Executor 负责执行 Java 和 数据库交互,在 MyBatis 中存在三种执行器,可以在配置文件的 setting 元素的 defaultExecutorType 指定
构造过程如下:
执行过程中,interceptorChain.pluginAll(executor) 就是 MyBatis 插件执行的地方,这行代码会为 executor 构建一层层代理对象,在调度真实的 Executor 方法之前执行配置插件的代码可以修改。SimpleExecutor 的查询处理过程如下所示:
如代码所示,SimpleExecutor 的查询过程一共有 4 步:
StatementHandler,顾名思义就是处理数据库会话的,其创建过程如下:
实际创建的是 RoutingStatementHandler 对象,它实现了 StatementHandler 接口。和 Executor 一样用代理对象做了一层层封装
但 RoutingStatementHandler 对象也不是实际处理查询任务的对象,它通过 适配模式 找到对应的 StatementHandler 来执行具体的数据库操作,MyBatis 也有三种 StatementHandler:SimpleStatementHandler、PreparedStatementHandler 以及 CallableStatementHandler
image
image
我们以最常用的 PreparedStatementHandler 为例讲解 StatementHandler 的三个主要方法:prepare、parameterize 和 query 是如何执行的
首先 Executor 调用 StatementHandler#prepare 进行 SQL 预编译
instantiateStatement 对 SQL 进行预编译,做了一些基础设置(超时、获取最大行数等),然后 Executor 调用 StatementHandler#parameterize 设置参数
可以看到这步操作是调用 ParameterHandler 完成的,2.2.3 中将详细描述参数设置过程。最后 Executor 调用 StatementHandler#query 执行查询
StatementHandler#query 执行 PreparedStatement#execute 执行 SQL,并将结果通过 ResultSetHandler 处理返回,详见 2.2.4
综上所述,我们就知道 MyBatis 处理一条 SQL 的执行过程了,整体过程如下图所示(以 SimpleExecutor 的查询为例):
image
image
下面我们再具体看看 ParameterHandler 和 ResultHandler 是如何设置参数并将结果封装返回的
ParameterHandler 是参数处理器,主要完成对预编译参数的设置。我们先看一下其接口定义:
ParameterHandler#getParameterObject 返回参数对象,ParameterHandler#setParameters 设置预编译 SQL 语句的参数
MyBatis 提供了 默认实现类 DefaultParameterHandler,我们主要看一下 setParameters 方法的实现
ResultHandler 负责处理 SQL 执行结果集,接口定义如下:
其中,handleResultSets 负责封装结果集。MyBatis 提供了实现类 DefaultResultSetHandler,它涉及到使用 JAVASSIST 或 CGLIB 作为延迟加载,然后通过 TypeHandler 和 ObjectFactory 进行组装再返回结果
SqlSession 内部运行图如下所示
image
image
SqlSession 通过 Executor 创建 StatementHandler 来执行 SQL 请求的。StatementHandler 要经过三个步骤:
其中 parameterize 调用 ParameterHandler 设置参数,参数类型根据类型处理器 TypeHandler 处理。query/update 方法通过 ResultHandler 进行结果封装,如果是 update 语句则返回整数,否则通过 TypeHandler 处理结果类型,然后用 ObjectFactory 提供的规则封装对象,返回给调用者
在 MyBatis 中使用插件必须实现 Interceptor 接口
可以看到,MyBatis 使用 模板模式 进行插件开发
插件初始化在 MyBatis 初始化的时候完成,关键代码如下:
MyBatis 在上下文初始化过程中,就开始读入插件节点和我们配置的参数,然后通过反射生成 Interceptor 实例,并调用 setProperties 方法设置我们配置的参数,最后将 interceptorInstance 保存到配置对象 configuration 中
所以插件的初始化是 MyBatis 初始化的时候完成的,这样有助于提高性能
插件使用的是 责任链模式,责任链处理的对象是 MyBatis 四大对象中的一个,在 InterceptorChain 中的每个 Interceptor 都有机会处理目标对象
1 在 SqlSession 四大对象初始化时,调用 InterceptorChain#pluginAll 为其嵌套生成代理对象
image
image
2 InterceptorChain#pluginAll 为目标类生成嵌套代理对象,即有多个拦截器,就为目标类嵌套生成代理类
image
image
3 在调用四大对象的方法时,会代用 InvocationHandler#invoke,由于 Plugin 实现了 InvocationHandler 接口,故实际调用 Plugin#invoke 方法
如果该方法是 @Intercepts 注解设置的拦截方法,则调用 Interceptor.intercept 进行拦截处理,否则直接反射调用 method.invoke(target, args)。因此在 Interceptor#intercept 中可以自定义实际的拦截逻辑
Invocation 封装了目标类 target、要代理的方法 method 以及 method 的参数 args,可通过 Invocation#proceed 完成方法的反射调用。具体代码如下
image
image
MetaObject 可有效读取或设置类属性,其关键的方法有三个:
MetaObject SystemMetaObject#forObject(Object object)
用于包装对象,获取目标对象 object 的 MetaObject
Object MetaObject#getValue(String name)
获取对象属性值,支持 OGNL
void MetaObject#setValue(String name, Object value)
设置对象属性值,支持 OGNL
对象导航图语言(Object Graph Navigation Language),简称 OGNL,是一种表达式语言
QueryTrancePlugin 插件用于在 SQL 中追加调用链路和应用名称
在应用的配置文件中,插件配置如下
Mybatis 插件原理解析的更多相关文章
- Mybatis插件原理分析(二)
在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析. ...
- mybatis 插件原理
[传送门]:mybatis 插件原理
- MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)(转)
在介绍MyBATIS插件原理前我们需要先学习一下一些基础的知识,否则我们是很难理解MyBATIS的运行原理和插件原理的. MyBATIS最主要的是反射和动态代理技术,让我们首先先熟悉它们. 1:Jav ...
- Mybatis框架(8)---Mybatis插件原理
Mybatis插件原理 在实际开发过程中,我们经常使用的Mybaits插件就是分页插件了,通过分页插件我们可以在不用写count语句和limit的情况下就可以获取分页后的数据,给我们开发带来很大 的便 ...
- android黑科技系列——微信抢红包插件原理解析和开发实现
一.前言 自从几年前微信添加抢红包的功能,微信的电商之旅算是正式开始正式火爆起来.但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导 ...
- Android中微信抢红包插件原理解析和开发实现
一.前言 自从去年中微信添加抢红包的功能,微信的电商之旅算是正式开始正式火爆起来.但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导 ...
- Mybatis插件原理分析(一)
我们首先介绍一下Mybatis插件相关的几个类,并对源码进行了简单的分析. Mybatis插件相关的接口或类有:Intercept.InterceptChain.Plugin和Invocation,这 ...
- Mybatis插件原理分析(三)分页插件
在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件. 虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBo ...
- MyBatis插件原理
官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#plugins MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认 ...
随机推荐
- 以Winsows Service方式运行JupyterLab
有数据分析,数据挖掘,以及机器学习和深度学习实践经验的读者应该会对Jupyter Notebook这一工具十分熟悉,而JupyterLab是它的升级版本,其提供了更具扩展性,更加可定制化的功能选项. ...
- 牛哄哄的celery
一.什么是Celery 1.1.celery是什么 Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度. Celery的架构由三部分组成, ...
- Python开发的入门教程(八)-迭代
介绍 本文主要介绍Python中迭代的基本知识和使用 什么是迭代 在Python中,如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们成为迭代(Ite ...
- 为什么golang中不存在三元运算符
三元运算符广泛存在于其他语言中,比如: python: val = trueValue if expr else falseValue javascript: const val = expr ? t ...
- 剑指 Offer 47. 礼物的最大价值
题目描述 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0).你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格.直到到达棋盘的右下角.给定一个棋盘及 ...
- 跟着兄弟连系统学习Linux-【day02】
day02-20200528 p6.vmvare安装与使用 官网下载安装包,个人学习的时候要求不高,所以不用安装最新版本,用不到那么多的功能,保证稳定版本就好了,然后傻瓜式安装.注意安 ...
- Python 零基础快速入门!
“人生苦短,我学python”是编程届的名言.用python写小脚本的便捷性,让很多其他语言的学习者把python当作辅助语言.拥有了某一个语言的功底,再来学习另外一种语言应该是十分快速的.编程理念都 ...
- 同样是logback1.11,更换了log配置后,无论是否有线程持续不断写入log文件,log文件会按设定以日期序号轮换
上次发现了logback1.11的一个bug,即有线程持续写入log,则log文件不会按设定模式进行轮换. 但发现同样采用logback1.11的另外一个工程,它的日志文件就没有错误,于是参照其配置文 ...
- 【Java】一句话获得你的IP地址
System.out.println(java.net.InetAddress.getLocalHost().getHostAddress());
- Linux:用tar解压文件出现错误Not found in archive
问题:用tar解压文件出现错误Not found in archive 解决办法:加上-C参数 tar -zxvf ZenTaoPMS.8.1.3.zbox_64.gz -C /usr 因为压缩文件 ...