插件原理与开发

插件原理与开发

在 Mybatis总体执行流程 一文中简单的介绍了插件的初始化过程,本文将从源码的角度介绍一下mybatis的插件原理与简单开发实战。

插件原理

插件的注册和管理是通过InterceptorChain进行的,在创建Executor、StatementHandler、ParameterHandler、ResultSetHandler对象时,会执行InterceptorChain的pluginAll方法

  public Object pluginAll(Object target) {// 遍历所有的插件for (Interceptor interceptor : interceptors) {// 执行插件的plugin方法,返回代理对象target = interceptor.plugin(target);}return target;}

拦截的原理,正是此时返回的代理对象,当调用目标方法时,执行的就是拦截器的intercept方法,从而实现拦截功能。

      // 执行插件的plugin方法,返回代理对象target = interceptor.plugin(target);

来到Interceptor接口的plugin方法:

  default Object plugin(Object target) {return Plugin.wrap(target, this);}

这是一个默认方法,一般不会重写它的逻辑。看其实现Plugin#wrap:

  public static Object wrap(Object target, Interceptor interceptor) {// 拿到拦截器的@Intercepts注解信息:key是要拦截的接口,value是要拦截的接口方法集合Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);// 这里的target,就是拦截的对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler对象)Class<?> type = target.getClass();// 返回包含在signatureMap中的接口Class<?>[] interfaces = getAllInterfaces(type, signatureMap);// 存在被拦截的接口,返回一个代理对象if (interfaces.length > 0) {// 利用jdk动态代理生成代理对象:关注Plugin(实现了InvocationHandler接口)的invoke方法return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}// 接口没有被拦截,返回原始对象return target;}

可以看到,如果接口被拦截了,就会利用JDK动态代理生成代理对象,由于Plugin实现了InvocationHandler接口,所以其invoke方法会被执行:

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 拿到被拦截的接口方法集合Set<Method> methods = signatureMap.get(method.getDeclaringClass());// 判断当前执行的方法是否包含在其中,包含就是被拦截的方法if (methods != null && methods.contains(method)) {// 执行自定义拦截器的intercept方法,并将目标对象、方法、参数传入return interceptor.intercept(new Invocation(target, method, args));}// 否则直接执行原始方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}

插件开发

自定义插件需要:

  1. 实现Interceptor接口,重写intercept方法

  2. 使用@Intercepts和@Signature注解表明需要拦截哪些类的哪些方法

  3. 在配置文件中,添加插件配置

mybatis官网中,对此也有所描述:mybatis – MyBatis 3 | Configuration

根据官网描述,mybatis插件可以拦截的方法如下:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

以下是我写的一个记录SQL及其耗时的拦截器,加深对拦截器的理解:

/*** @Author: qiuxinfa* @CreateTime: 2023-12-07  22:15* @Description: 自定义拦截器:打印SQL、统计SQL执行时间*/
@Intercepts({@Signature(type = StatementHandler.class,method = "batch",args = {Statement.class}),@Signature(type = StatementHandler.class,method = "update",args = {Statement.class}),@Signature(type = StatementHandler.class,method = "query",args = {Statement.class, ResultHandler.class}),
})
public class SqlLogPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取执行的SQLString sql;Statement statement=(Statement) invocation.getArgs()[0];if(Proxy.isProxyClass(statement.getClass())){MetaObject metaObject= SystemMetaObject.forObject(statement);Object h = metaObject.getValue("h");if(h instanceof StatementLogger){RoutingStatementHandler rsh=(RoutingStatementHandler) invocation.getTarget();sql = rsh.getBoundSql().getSql();}else {PreparedStatementLogger psl=(PreparedStatementLogger) h;sql = psl.getPreparedStatement().toString();}}else{sql = statement.toString();}// 记录开始时间long start = System.currentTimeMillis();// 执行目标方法Object result = invocation.proceed();// 记录结束时间long end = System.currentTimeMillis();System.err.println("执行SQL ===> ");System.err.println(sql);System.err.println("统计SQL耗时 = " + (end - start) + "毫秒");System.err.println("返回结果 =======> " + result);return result;}
}

配置文件添加插件:

    <!-- 配置插件    --><plugins><plugin interceptor="com.qxf.plugin.SqlLogPlugin"></plugin></plugins>

配置之后,会打印执行的SQL语句及其耗时。

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

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

相关文章

1.求两个数最大值

文章目录 前言一、题目描述 二、解题 程序运行代码 前言 本系列为函数编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 1.求两个数最大值 二、解题 程序运行代码 #include<stdio.h> int main(){int a,b,z;scanf("%d%d",&a,&b);if…

如何使用 Docker 安装 Node-RED

安装 Node-RED 使用 Docker 是一种简便的方式&#xff0c;以下是基本的步骤&#xff1a; 安装 Docker&#xff1a; 确保已在系统上安装 Docker。可从 Docker 官方网站 或 Windows Docker 安装教程 获取安装指南。 拉取运行 Node-RED 镜像&#xff1a; 打开终端或命令行界面&am…

Matlab 点云对称性检测

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 这是一个很有趣的功能,它的思路其实与ICP算法的思路有些相似: 首先,它会初始化两个旋转角度,即绕x轴旋转与绕y轴旋转,初始的过程是将点对称(镜像)过去,计算与匹配点之间的距离误差,误差最小者为最优初始值…

Python 从入门到精通 学习笔记 Day02

Python 从入门到精通 第二天 今日目标 字符串基本操作、字符串序列操作、输入输出函数 字符串内置方法、运算符、练习之前学习的内容 一、字符串基本操作 在Python中&#xff0c;字符串的转义是指在字符串中使用特殊的字符序列来表示一些特殊字符。 在Python中&#xff0c;字…

2024山东健博会,第六届济南国际大健康产业博览会5月举办

大力发展全国健康事业 助力健康中国行动战略 DJK 2024第6届中国&#xff08;济南&#xff09;国际大健康产业博览会The 2024 sixth China (Jinan) International Big Health Industry Expo 2024年05月27日—29日 中国济南黄河国际会展中心 联合主办&#xff1a;山东省科学养生…

低代码你需要了解一下

低代码的概念可以追溯到1980年代&#xff0c;当时IBM的快速应用程序开发工具&#xff08;RAD&#xff09;被冠以新的名称——低代码&#xff0c;由此&#xff0c;低代码的概念首次面向大众。然而&#xff0c;在近40年的历程中&#xff0c;低代码发展经历了两个阶段&#xff1a;…

数据库基础DDL

一、字段属性 合适的字段类型对于高性能来说非常重要&#xff0c;基本原则如下&#xff1a;简单的类型占用资源更少&#xff1b;在可以正确存储数据的情况下&#xff0c;选最小的数据类型。 1、数据类型选择 整数类型 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT&#xff0…

改进的联邦加权平均算法

目录 1 改进的联邦加权平均算法 1.1 联邦学习 1.2 改进的联邦加权平均算法 1.3 算法的流程

vue watch

vue 使用watch监听props的一些小建议 当在watch里面给data赋值&#xff0c;请使用深拷贝。 <template><div class"container"><div class"left"><div class"button_group"><!-- <button click"rand…

【面试常考150题】1、88合并两个有序数组

合并两个有序数组 题目解法一思路code 解法二思路code 题目 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递…

力扣78. 子集(java 回溯解法)

Problem: 78. 子集 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 我们易知&#xff0c;本题目涉及到对元素的穷举&#xff0c;即我们可以使用回溯来实现。对于本题目我们应该较为注重回溯中的决策阶段&#xff1a; 由于涉及到对数组中元素的穷举&#xff0c;即在每…

Leetcode刷题笔记题解(C++):165. 比较版本号

思路&#xff1a;利用小数点来分开求和&#xff0c;判断和是否相等来推断两个版本号是否一致 class Solution { public:int compareVersion(string version1, string version2) {int i0;//记录version1的字符下标int j0;//记录version2的字符下标int num10;//保存version1的和…

对于并发修改同一条数据的处理方案

业务场景&#xff1a; 批量消费Kafka数据&#xff0c;一个批次针对同一条数据做新增和修改操作&#xff0c;当前一条数据还未更新完成&#xff0c;后一条数据也同步修改&#xff0c;数据就存在异常&#xff1b; 单机模式&#xff1a; &#xff08;一&#xff09…

Unity传送门特效: The Beautiful Portal/Level up/Teleport/Warp VFX

7种不同风格的传送门特效! 每个传送门都有一个轻型和重型版本。 每个版本都有一个"无循环”和一个"无限”预制件:D 总共有28个预制件 -VFX完全使用Unity的粒子系统和基本的Unity着色器。 使用标准渲染管道中制作了这个资产。所以VFX的功能就像视频宣传片一样。 同时,…

怎样用Ajax提交from表单并接收其中的json数据

怎样用Ajax提交表单并接收其中的json数据 需求&#xff1a;实现点击按钮后&#xff0c;数据以表单形式提交至服务器&#xff0c;并接收来自服务器的返回数据。过程中页面不刷新。 AJAX 不是新的编程语言&#xff0c;而是一种使用现有标准的新方法。AJAX 是与服务器交换数据并…

解析5种常用的Python设计模式

大家好&#xff0c;Python是一种非常流行的编程语言&#xff0c;它易于学习、灵活且功能强大&#xff0c;在各个领域广泛应用。很多人误认为Python是一种面向过程的语言&#xff0c;无法很好地支持面向对象的编程风格。这种观念是错误的&#xff0c;Python不仅支持面向对象编程…

Spring AOP从入门到精通

目录 1. AOP的演化过程 1. 代理模式 2. 动态代理 2.1 JDK动态代理 2.2 Cglib动态代理 3. Spring模式 3.1 ProxyFactory 3.2 ProxyFactoryBean 3.3 AbstractAutoProxyCreator 2. Spring AOP抽象 1. 核心术语 1.1 连接点(JoinPoint) 1.2 切点(Pointcut) 1.3 增强(Ad…

C# Solidworks二次开发:三种获取SW设计结构树的方法-第二讲

今天这篇文章是接上一篇文章的&#xff0c;主要讲述的是获取SW设计结构树节点的第二种方法。 这个方法获取节点的逻辑是先获取最顶层节点&#xff0c;然后再通过获取顶层节点的子节点一层一层的把所有节点都找出来&#xff0c;也就是需要递归。想要用这个方法就要了解下面几个…

tomcat环境搭建

镜像下载地址&#xff1a;https://mirror.tuna.tsinghua.edu.cn/apache/tomcat/ 配置环境变量 添加系统变量 编辑Path 测试 dos窗口运行startup启动tomcat 访问http://localhost:8080/

【LeeCode】454. 四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < n nums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2],…