2.3MyBatis——插件机制

news2024/10/6 2:09:44

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 {
    @Override
    public 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
@ConditionalOnMissingBean
 public 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.coloradmin.cn/o/2190945.html

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

相关文章

【AI大模型】使用Embedding API

一、使用OpenAI API 目前GPT embedding mode有三种&#xff0c;性能如下所示&#xff1a; 模型每美元页数MTEB得分MIRACL得分text-embedding-3-large9,61554.964.6text-embedding-3-small62,50062.344.0text-embedding-ada-00212,50061.031.4 MTEB得分为embedding model分类…

Linux驱动开发(速记版)--printctl子系统

第102章 pinctrl 子系统的引入 Linux中的 pinctrl子系统是管理和配置GPIO引脚的框架&#xff0c;提供标准化方法以适应不同硬件。 它遵循 Linux内核设备模型&#xff0c;分为设备、驱动等部分。 本章节从设备和驱动角度介绍 pinctrl子系统。 102.1 pinctrl 设备树 在设备树中&a…

旅游心动盲盒:开启个性化旅行新体验

嘿&#xff0c;宝子们&#xff01;在如今这个数字化时代呀&#xff0c;文心智能体可是给咱们的生活带来了超多便利和创新呢。今天呀&#xff0c;我来给大家介绍一款超棒的智能体——旅游心动盲盒&#xff0c;它肯定能给你的旅行带来全新的惊喜和超个性化的体验哟。 一、项目背…

基于H3C环境的实验——OSPF

目录 实验设备和环境 实验设备 实验环境 实验记录 1、单区域 OSPF基本配置 步骤1:搭建实验环境并完成基本配置 步骤2:检查网络连通性和路由器路由表。 步骤3:配置OSPF 步骤4:检查路由器OSPF邻居状态及路由表 实验设备和环境 实验设备 三台路由器、两台PC、电源线、两…

GO网络编程(四):海量用户通信系统2:登录功能核心【重难点】

目录 一、C/S详细通信流程图二、消息类型定义与json标签1. 消息类型定义2. JSON标签3.结构体示例及其 JSON 表示&#xff1a;4.完整代码与使用说明 三、客户端发送消息1. 连接到服务器2. 准备发送消息3. 创建 LoginMes 并序列化4. 将序列化后的数据嵌入消息结构5. 序列化整个 M…

java 数据存储方式

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

有状态(Session) VS 无状态(Token)

目录 概念 JWT Token在项目中使用 概念 有状态和无状态服务是两种不同的服务架构&#xff0c;两者的不同之处在于对于服务状态的处理。 1、有状态服务 是指程序在执行过程中生成的中间数据&#xff0c;服务器端一般都要保存请求的相关信息&#xff0c;每个请求可以默认地使…

Hugging Face 任意大模型仓库劫持 - 无声的破坏

摘要 在这篇博客中&#xff0c;我们展示了攻击者如何攻击Hugging Face的Safetensors转换空间及其相关的服务机器人。这些服务是一个在Hugging Face上广受欢迎的服务&#xff0c;专门用于将不安全的机器学习模型转换为更安全的版本。我们随后展示了如何通过Hugging Face自身的服…

C0016.Clion中qDebug()打印输出中文时,都是问号??????的解决办法

问题描述 在clion中使用qDebug打印输出中文内容时&#xff0c;都是&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;如下图&#xff1a; 注意&#xff1a;修改该文件的编码格式就行&#xff0c;该文件名为apr.cpp&#xff1b; 解决办法

矩阵求解复数(aniwoth求解串扰)

所以这种求解串扰的格式是因为&#xff0c;有串扰的共轭项在方程组中 复数共轭项的作用&#xff0c;但是这是二次方程&#xff0c;

vue2集成vuex实现网站统一数据管理

文章目录 前言安装配置过程1、安装vuex依赖2、在src目录下创建store文件夹&#xff0c;创建模块site.jsgetters.jsindex.js 3、在man.js中添加vuex vuex实战&#xff1a;存储与获取网站基础数据何时去存储数据&#xff1f;&#xff08;路由前置获取数据&#xff09;如何取数据&…

windows中下载、安装、配置JDK/JDK环境配置/Java配置环境变量/Linux中安装配置JDK环境

JDK下载(官网)、安装、配置(包括系统、idea、eclipse)一篇就够了 1、问题概述? Java开发者必须掌握的JDK下载、安装、配置过程。 包括在Eclipse及IDEA中的配置使用 2、下载JDK 【注册Oracle官网账号】 下载的前天是注册orcle官网账号,作为开发者,这个必须有,随时关注…

RTX4060安装nvidia显卡驱动

文章目录 nvidia drivers下载删除原有nvidia驱动安装nvidia驱动如果报错Unable to find the kernel source tree for the currently runningbuilding kernel modules解决方法 报错成功安装!!! nvidia drivers下载 https://www.nvidia.cn/geforce/drivers/#:~:textNVIDIA%20GeF…

ESP01 AT指令学习

一 、AT指令 测试指令&#xff1a;ATCWMODE? 参数及取值范围 cwmode&#xff08;1-3&#xff09; 查询指令&#xff1a; ATCWMODE&#xff1f; 当前cwmode的取值 3 设置指令&#xff1a; ATCWMODE3 设置当前的cwmode为 3 1、station 模式 连接到其他wifi 2、softA…

【源码+文档】基于SpringBoot+Vue的酒店管理系统

&#x1f6a9;如何选题&#xff1f; 如何选题、让题目的难度在可控范围&#xff0c;以及如何在选题过程以及整个毕设过程中如何与老师沟通&#xff0c;这些问题是需要大家在选题前需要考虑的&#xff0c;具体的方法我会在文末详细为你解答。 &#x1f6ad;如何快速熟悉一个项目…

如何从 Windows 照片库恢复已删除的照片

数据丢失的主要原因之一是人为错误。更糟糕的是&#xff0c;回收站中没有备份或删除的文件。在这种情况下&#xff0c;数据恢复或适用于 Windows 的专用图片恢复工具可以为您提供帮助&#xff0c;因为它们可以帮助恢复已删除的图片。 牢记这一点&#xff0c;我们将讨论从 Wind…

基于SSM的电影院订票系统设计与实现

文未可获取一份本项目的java源码和数据库参考。 开展本课题的意义及工作内容&#xff1a; 1.意义 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于电影院订票系统系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如电影信息管理、订单管理等&#xff0c;…

04-SpringBootWeb案例(中)

3. 员工管理 完成了部门管理的功能开发之后&#xff0c;我们进入到下一环节员工管理功能的开发。 基于以上原型&#xff0c;我们可以把员工管理功能分为&#xff1a; 分页查询&#xff08;今天完成&#xff09;带条件的分页查询&#xff08;今天完成&#xff09;删除员工&am…

压摆率(Slew Rate)

1. 定义 压摆率&#xff08;Slew Rate&#xff09;也叫转换速率&#xff0c;是指运算放大器输出电压的最大变化速率&#xff0c;单位通常为伏特每微秒&#xff08;V/s&#xff09;。 如果输入信号的变化速率超过运算放大器的压摆率&#xff0c;输出信号将不能即时跟随输入信号…

多线程编程实例

代码&#xff1a; #include<stdio.h> #include<pthread.h> static int run-1; static int retvalue; void *start_routine(void *arg) {int *running arg;printf("child Thread initial over,pass in parameters:%d\n",*running);while(*running){print…