2.3MyBatis——插件机制

2.3MyBatis——插件机制

  • 1.基本用法
  • 2.原理探究
    • 2.1加载过程
    • 2.2执行过程
      • 2.2.1 插件的执行点
      • 2.2.2 SQL执行的几个阶段
      • 2.2.3 如何梳理出执行流程

插件机制是一款优秀框架不可或缺的组成部分,比如spring、dubbo,还有我们要聊的Mybatis等等。所谓插件,通俗一点说,就是框架提供了一个入口,允许你通过实现框架提供的扩展接口,来进行功能增强。比如spring的前置处理器允许你在Bean创建的过程中添加自定义逻辑。


具体到Mybatis框架,它的核心是ORM和SQL的映射。那么映射成功的SQL在具体执行的过程中,存在许多时机,比如参数处理阶段,SQL语句处理阶段,SQL执行阶段,返回结果的处理阶段等。在这些阶段或者说执行点,如果框架提供了扩展点,那么我们就可以通过插件在相应的环节添加一些能力。

1.基本用法

因为Mybatis的插件并不像SQL映射那样被经常使用,所以先看一下插件的基本用法。根据Mybatis官方的文档说明,只要实现Interceptor接口,然后注册插件即可。

//测试使用Springboot,插件放入Springboot容器后通过ObjectProvider注入到Configuration对象中完成注册
@Component 
@Intercepts({ // 声明作用点,即拦截哪些方法@Signature(type = Executor.class, // 这里拦截执行器的query方法method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("自定义插件:统计运行时长");long begin = System.currentTimeMillis();Object result = invocation.proceed();long end = System.currentTimeMillis();System.out.println((end - begin) /1000 + "s");return result;}
}

这里定义拦截器的方式类似定义切点,type的常用取值为(下文会详细介绍):

  • Executor
  • StatementHandler
  • ParameterHandler
  • ResultSetHandler

这些接口涵盖了SQL执行的各个阶段。method取值则是type接口中对应的方法,如果方法存在重载,还可以通过args进行限定。

2.原理探究

当mapper接口中的方法执行时,插件被调用并统计时长。关于Mybatis插件,使用时很自然的疑问是:它是如何被加载注册,又是如何被调用执行的(即它的设计原理是什么)?如果你没有疑问,那就现在发出疑问…
然后我们从这两点去分析和学习。

2.1加载过程

我们知道在Mybatis中,不管是以哪种方式配置Mybatis,Mybatis的配置信息和mapper.xml的数据最终都会以Configuration对象的形式存在,Configuration它是Mybatis的核心组件之一。
如果不使用Springboot,集成Mybatis需要创建相应的配置文件,在配置文件中进行Mybatis的相关配置,包括插件注册。所以猜想插件信息也是被收集到Configuration对象中。我们浅浅看一下代码:

// 1. configuration对象提供了添加拦截器的方法
public void addInterceptor(Interceptor interceptor) {interceptorChain.addInterceptor(interceptor);}
// 2 XML 配置文件解析时会调用addInterceptor,解析后通过add进行注册
pluginElement(root.evalNode("plugins"));// 解析plugins节点// 3.1 springboot 项目中,通过ObjectProvider获取所有的Interceptor实现类,然后完成注册
public MybatisAutoConfiguration(ObjectProvider<Interceptor[]> interceptorsProvider,....) {this.interceptors = interceptorsProvider.getIfAvailable();.....}
@Bean
@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
...
if (!ObjectUtils.isEmpty(this.interceptors)) {factory.setPlugins(this.interceptors);// 注册所有拦截器实现类
}
...
}

这里只需要明白一点,拦截器的实现类(即插件),最终都会通过addInterceptor被添加到核心组件Configuration对象的interceptorChain属性中。其他的诸如SQL映射(即mappedStatements)、转换器等都是同样的加载逻辑。

2.2执行过程

2.2Mybatis——代理与SQL映射这篇博客中,只聊到动态代理MapperProxy是如何将mapper接口与mapper.xml进行关联的。至于SQL的后续执行没有描述,下面我们通过研究插件的执行,顺带了解一下SQL的执行过程

2.2.1 插件的执行点

既然是介绍插件机制,自然是以插件的执行作为切入点。由于Mybatis的执行过程中涉及多个代理对象的调用,如果在调试过程中发现程序反复横跳,可以先不纠结,你应该至少能发现一点:在Configuration对象中,有多个方法调用了pluginAll

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);// 遍历执行插件逻辑parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}// newResultSetHandler newStatementHandler newExecutor// pluginAll
public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;
}

newResultSetHandler newStatementHandler newExecutor newParameterHandler四个方法中,都调用了pluginAll,也就是说在这些方法被调用时,都可以植入插件逻辑。

2.2.2 SQL执行的几个阶段

如果想要精确控制插件在SQL执行的哪个环节生效,就必须要知道Mybatis中SQL执行分几个阶段,并且每个阶段和newResultSetHandler newStatementHandler newExecutor newParameterHandler的对应关系。
SQL执行的先后顺序 :

  1. 创建执行器对象,对应 newExecutor()
  2. 创建SQL语句对象,对应 newStatementHandler()
  3. 参数处理器, 对应newParameterHandler()
  4. 结果集处理器,对应newParameterHandler()

知道了各个阶段和对应的方法,这样才能精确的控制拦截器生效的时机。比如文章开头定义的插件,它将作用在newExecutor()执行时,即SQL执行的最开始阶段。

2.2.3 如何梳理出执行流程

前面提到,插件的执行流程中,由于涉及到多个代理对象的调用,所以调试流程不像普通的栈帧嵌套。调试过程如果觉得不顺,建议多去理解2.2Mybatis——代理与SQL映射这篇文章中关于JDK的代理实现。代理对象对接口方法的调用就是调用处理器中invoke方法的执行。明白了这一点,上述的插件执行点和SQL执行阶段都可以梳理出来。我们简单分析一下流程:

  1. MapperProxy确定SQL映射关系后,调用MapperMethod.execute方法,后面开始执行SQL
    在这里插入图片描述
  2. 由于使用的是mybatis-spring-boot-starter依赖,所以这里的sqlSessionSqlSessionTemplate实例,可以看到,SqlSessionTemplate类中持有的sqlSessionProxy就是一个JDK代理对象,所以this.sqlSessionProxy.selectOne()实际是调用SqlSessionInterceptorinvoke方法
    在这里插入图片描述
  3. 继续调试,当调用拦截器链的pluginAll()方法时,内部是调用拦截器的plugin方法。MyBatis内部有Plugin插件类,为啥自定义插件却要实现拦截器接口呢?(拦截器就是插件的逻辑部分)
    在这里插入图片描述
  4. Plugin对象也是一个调用处理器,即上面代码中Plugin.wrap返回的是一个代理对象,在调用真实对象之前执行代理逻辑,这里的代理逻辑就是拦截器逻辑
    在这里插入图片描述
  5. 看到这里你应该明白,方法依次返回后,pluginAll返回的是一个JDK代理对象,executor为例,代理对象代理了executor,且代理的逻辑就是在executor执行指定方法前,调用插件逻辑(第四步invoke逻辑),这就是Mybatis插件的原理
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1555406.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vSAN02:容错、存储策略、文件服务、快照与备份、iSCSI

目录 vSAN容错条带化存储策略1. 创建新策略2. 应用存储策略 vSAN文件服务文件服务快照与备份 vSAN iSCSI目标服务 vSAN容错 FTT&#xff1a;Fault to Tolerance 允许故障数 故障域&#xff1a;每一台vSAN主机是一个故障域 - 假设3台超融合&#xff08;3计算1存储&#xff09;&…

力扣 简单 110.平衡二叉树

文章目录 题目介绍解法 题目介绍 解法 平衡二叉树:任意节点的左子树和右子树的高度之差的绝对值不超过 1 //利用递归方法自顶向下判断以每个节点为根节点的左右子树的最大深度是否大于1 class Solution {public boolean isBalanced(TreeNode root) {if(root null){return tr…

基于Java的GeoTools对Shapefile文件属性信息深度解析

目录 前言 一、Shapefile的属性列表信息 1、属性表格信息 2、属性表格包含的要素 二、GeoTools对属性表格的解析 1、常规解析方法 2、基于dbf文件的属性信息读取 三、总结 前言 ESRI Shapefile&#xff08;shp&#xff09;&#xff0c;或简称shapefile&#xff0c;是美…

【Spring Boot React】Spring Boot和React教程 完整版

【Spring Boot & React】Spring Boot和React教程 在B站找到一个不错的SpringBoot和React的学习视频&#xff0c;作者是amigoscode 【Spring Boot & React】Spring Boot和React教程 2023年更新版【Spring Boot React】价值79.9美元&#xff0c;全栈开发&#xff0c;搭…

如何使用CMD命令启动应用程序(二)

说明&#xff1a;去年1024发布了一篇博客&#xff0c;介绍如何使用CMD命令启动应用程序&#xff0c;但实际情况&#xff0c;有些程序可能无法用配置环境变量的方式来启动&#xff0c;本文针对两种情况下的程序&#xff0c;如何使用CMD命令来启动&#xff0c;算是对上一篇博客的…

Qt操作主/从视图及XML——实例:汽车管理系统

目录 1. 主界面布局2.连接数据库3.主/从视图应用 1. 主界面布局 先创建一个QMainwindow&#xff0c;不带设计界面 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QGroupBox> #include <QTableView> #include <QListWidg…

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…

如何解决 MySQL ERROR 1040 (08004): Too many connections ?

MySQL 是最流行的开源关系数据库管理系统之一&#xff0c;它也是开发人员中非常常用的数据库。即便它高度健壮和可伸缩性极强&#xff0c;像任何软件一样&#xff0c;它也可能出现错误。我们会经常遇到一个错误&#xff0c;特别是在高流量系统中&#xff0c;error 1040 (08004)…

51c视觉~CV~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/11668984 一、 CV确定对象的方向 介绍如何使用OpenCV确定对象的方向(即旋转角度&#xff0c;以度为单位)。 先决条件 安装Python3.7或者更高版本。可以参考下文链接&#xff1a; https://automaticaddison.com/how-to-s…

阳台山足球营地的停车位探寻

阳台山足球营地的环境是真好哈。停车场名称&#xff1a;阳台山森林公园配套停车场。应该很多爬山的人车子也停在这里。而且我没想到的&#xff0c;阳台山的山泉水还有不少居民每天提着空桶去山上装。看来环境是真的很好哈 停车场有地面和地下停车场&#xff0c;停车位个数查不…

java 数据存储方式

1. 变量存储 这是最基本的数据存储方式&#xff0c;通过声明变量来存储数据。变量可以是基本数据类型&#xff08;如int、float、char等&#xff09;&#xff0c;也可以是引用数据类型&#xff08;如对象、数组等&#xff09;。变量存储的数据通常存储在内存中&#xff0c;随着…

【记录】Excel|Excel 打印成 PDF 页数太多怎么办

【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题 文章目录 【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题方法一&#xff1a;调整页边距WPS OfficeMicrosoft Excel 方法二&#xff1a;优化页面布局调整列宽和行高使用“页面布局”视图合并单…

python全栈开发是什么?

全栈指掌握多种技能&#xff0c;并能利用多种技能独立完成产品。通俗的说就是与这项技能有关的都会&#xff0c;都能独立完成。 python&#xff0c;因为目前很火&#xff0c;能开发的项目很多。例如&#xff1a;web前端后端&#xff0c;自动化运维&#xff0c;软件、小型游戏开…

八大排序--01冒泡排序

假设有一组数据 arr[]{2&#xff0c;0&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;7} 方法&#xff1a;开辟两个指针&#xff0c;指向如图&#xff0c;前后两两进行比较&#xff0c;大数据向后冒泡传递&#xff0c;小数据换到前面。 一次冒泡后&#xff0c;数组中最大…

【网络篇】计算机网络——应用层详述(笔记)

目录 一、应用层协议原理 1. 进入应用层 2. 网络应用程序体系结构 &#xff08;1&#xff09;客户-服务器体系结构&#xff08;client-server architecture&#xff09; &#xff08;2&#xff09; P2P 体系结构&#xff08;P2P architecture&#xff09; 3. 进程间通讯 …

【GC日志和OOM日志分析】JVM GC日志和OOM Dump文件分析

1 缘起 充电、充电、充电。 增加一些必备的知识&#xff0c;帮助后续使用。 2 配置JVM参数 为分析GC日志以及OOM相关信息&#xff0c;配置JVM参数&#xff0c;分为三个部分&#xff1a; &#xff08;1&#xff09;堆内存&#xff0c;包括年轻代、最大堆内存&#xff1b; &a…

PELT算法

PELT算法的范畴 PELT算法&#xff08;Pruned Exact Linear Time&#xff09;属于时间序列分析和变点检测&#xff08;Change Point Detection&#xff09;范畴的算法。 从更广泛的角度来看&#xff0c;PELT算法还可以归类为以下几类算法的子集&#xff1a; 1. 时间序列分析&…

初识Linux · 文件(1)

目录 前言&#xff1a; 回顾语言层面的文件 理解文件的预备知识 文件和磁盘 使用和认识系统调用函数 前言&#xff1a; 本文以及下篇文章&#xff0c;揭露的都是Linux中文件的奥秘&#xff0c;对于文件来说&#xff0c;初学Linux第一节课接触的就是文件&#xff0c;对于C…

COMP 9517 Computer Vision week3

目录 特征表示图像特征概念(image feature)图像特征应该具备的属性 图像特征种类颜色特征颜色直方图(Color Histogram)颜色矩(Colour moments) 纹理特征(texture features)Haralick特征局部二值模式(Local Binary Patterns, LBP)尺度不变特征变换SIFT(Scale-invariant feature …

Error while loading conda entry point: conda-libmamba-solver

问题 解决方法 conda install --solverclassic conda-forge::conda-libmamba-solver conda-forge::libmamba conda-forge::libmambapy conda-forge::libarchive