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 允许你在已映射语句执行过程中的某一点进行拦截调用.默认 ...
随机推荐
- linux网络配置及虚拟机连接不上网排错思路
第1章 操作系统与虚拟软件的使用 1.1 虚拟软件使用方法 Vmware 1.1.1 开启vmware 注: 同时只能开启一个VMware软件,如果开了两个VMware窗口 提示 ...
- 使用 C# 捕获进程输出
使用 C# 捕获进程输出 Intro 很多时候我们可能会需要执行一段命令获取一个输出,遇到的比较典型的就是之前我们需要用 FFMpeg 实现视频的编码压缩水印等一系列操作,当时使用的是 FFMpegC ...
- openCV - 5~7 图像混合、调整图像亮度与对比度、绘制形状与文字
5. 图像混合 理论-线性混合操作.相关API(addWeighted) 理论-线性混合操作 用到的公式 (其中 α 的取值范围为0~1之间) 相关API(addWeighted) 参数1:输入图像M ...
- 数字电路基础(二)TTL与非门输入端悬空和接大电阻的问题
引言 我们在做那些判断与非门输入输出的时候,常常把输入端悬空和接大电阻作为高电平输入处理,比如下边这一例题: 很显然,我们无法直接从与非门逻辑图中看出其内部工作原理,那我们该如何分析呢?那肯定是去分析 ...
- 如何使用Xdebug单步调试PHP游戏服务器
参考文章:https://www.sourcetoad.com/resources/debugging-php-save-time-with-xdebugs-remote-autostart/ 配置参 ...
- 深入了解Netty【二】零拷贝
引言 以下翻译自:Zero Copy I: User-Mode Perspective 零拷贝是什么? 为了更好地理解问题的解决方案,我们首先需要理解问题本身.让我们来看看什么是参与网络服务器的简单过 ...
- 关于Vue的那些事儿
Vue 渐进式框架 众前端周知,Vue是一套用于构建用户界面的渐进式框架,自底向上逐层应用,关注视图层.那我们就来说道说道: 渐进式:声明式渲染->组件系统->客户端路由(router)- ...
- leetcode刷题-36有效的数独
题目 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. 数字 1-9 在每一行只能出现一次.数字 1-9 在每一列只能出现一次.数字 1-9 在每一个以粗实线分隔 ...
- pwnable.kr之flag
拿到文件,先运行一下,输出: I will malloc() and strcpy the flag there. take it. 用python查看文件是否有什么保护, Arch: amd64-- ...
- appium的基本环境配置
1.先安装jdk1.7或jdk1.8之后,并配置好环境变量 2.安装Android SDK(安卓软件开发包),测试Android手机必然要安装Android SDK,例如android-sdk_r24 ...