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 允许你在已映射语句执行过程中的某一点进行拦截调用.默认 ...
随机推荐
- py_二分查找
''' 查找:在一些数据元素中,通过一定的方法找出与关键字相同元素的过程, 列表查找:从列表中查找指定元素 输入:列表.待查找元素 输出:元素下标(未找到元素时一般返回None或-1) 内置列表查找函 ...
- Qt QDialog添加最大化和最小化按钮
Qt QDialog添加最大化和最小化按钮(转载) QDialog窗体右上角默认是没有最小化和最大化按钮的. 1.效果 2.上代码 1 // 设置窗体最大化和最小化 2 Qt::WindowFlags ...
- 我们真的需要JWT吗?
JWT(JSON Web Token)是目前最流行的认证方案之一.博客园.各种技术公众号隔三差五就会推一篇JWT相关的文章,真的多如牛毛.但我对JWT有点困惑,今天写出来跟大家探讨探讨,不要喷哈. J ...
- Node.js调试相关
如何进行Nodejs性能分析? nodejs性能最重要的两个部分:CPU耗时查看和内存泄漏排查 一,CPU相关 主要思路是两个:借助第三方的工具,以及借助v8自带的性能分析工具 借助第三方的工具 主要 ...
- Android开发java程序员常用代码,将字符串以逗号分别取出字符串String
public class StringSplit { public static void main(String[] args) { String sourceStr = "1,2,3,4 ...
- kotlin 作用域函数 : let、run、with、apply、 also、takeIf、takeUnless
1.官方文档 英文: https://kotlinlang.org/docs/reference/scope-functions.html 中文: https://www.kotlincn.net/d ...
- 面试【JAVA基础】Web与网络
1.转发与重定向的区别 转发是服务器请求资源,服务器直接访问目标地址url,把响应内容返回给浏览器. 重定向根据服务器返回的状态码重新请求地址. 转发是服务器行为,重定向是客户端行为. 转发显示的ur ...
- 模拟CMOS集成电路 课后习题总结(2.1)
前几天开始自学拉扎维的模设教材,看之前浏览了EETOP论坛里面好多大神们对这本书的看法,当然也有人在抱怨,比如冒出“太科幻”.“一年才看完”之类恐怖的修饰语句,因此在开始看的时候就对此书充满了“敬畏” ...
- C# 发送邮件通知
web.config <!-- 配置发邮件:邮件访问登录地址--> <add key="HttpsUrl" value="http://localhos ...
- leetcode刷题-75颜色分类
题目 给定一个包含红色.白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色.白色.蓝色顺序排列. 此题中,我们使用整数 0. 1 和 2 分别表示红色.白色和蓝 ...