Mybatis学习之插件

Mybatis学习之插件

Plugins

Mybatis中的插件虽然名称叫插件,但实质上是通过动态代理实现的。和我们平时讲的插件概念不一样,但是本质上都是给外部提供接口进行扩展。

MyBatis 允许我们在映射语句执行过程中的某一点进行拦截调用。MyBatis允许我们使用插件来拦截的方法调用包括:

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

自定义插件

在Mybatis中自定义插件只需要以下几步:

  1. 创建自定义插件类实现Interceptor接口;
  2. 使用@Intercepts注解:指定插件拦截四大对象中指定对象的指定方法;
  3. 在全局配置文件中注册插件

自定义插件代码如下:

这里使用@Intercepts注解指定了拦截Executor对象的update方法和query方法,由于接口中存在重载方法,所以通过args指定方法的参数来确定是哪一个方法。

@Intercepts({
        @Signature(type = Executor.class,method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class,method = "query", args = { MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class })})
public class MyFirstPlugin implements Interceptor {


    /**
     * 拦截目标对象的目标方法的执行
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("要拦截的方法: " + invocation.getMethod());
        //得到当前对象
        Object target = invocation.getTarget();
        System.out.println("拦截的对象:" + target);
        MetaObject metaObject = SystemMetaObject.forObject(target);
        Object[] args = invocation.getArgs();

        if (args[0] instanceof MappedStatement) {
            MappedStatement statement = (MappedStatement) args[0];
            System.out.println("=====================statement: " + statement);
            System.out.println("=====================statement id: " + statement.getId());
            System.out.println("=====================statement paraneterMap: " + statement.getParameterMap());
        }

        //执行原来的方法
        Object prObject = invocation.proceed();

        return prObject;
    }

    /**
     * 包装目标对象的:包装:为目标对象创建一个代理对象
     *
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        System.out.println("包装的对象: " + target.toString());
        return Plugin.wrap(target, this);
    }

    /**
     * 将插件注册时的property属性设置进来
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
        System.out.println("读取到配置文件的参数是:" + properties);
    }
}

将插件注册到全局配置文件中
在plugin标签中可以使用property设置要传递的参数,这里设置的参数,可以在setProperties方法中获取到。

    <plugins>
        <plugin interceptor="com.ruoyi.web.test.MyFirstPlugin">
            <property name="root" value="jjc"/>
            <property name="sdsdfs" value="123456"/>
        </plugin>
    </plugins>

插件原理

以上面的代码为例,在启动的时候,XMLConfigBuilder中的pluginElement方法会解全局配置文件中plugins节点。
在这里插入图片描述

在这里插入图片描述

configuration的addInterceptor方法最后会把拦截器加入到拦截器链中;而拦截的时候也会遍历拦截器链中的每一个拦截器,调用拦截器的plugin方法进行拦截。
在这里插入图片描述

resolveClass方法根据全限定类名解析出Class对象
在这里插入图片描述
Mybatis会尝试先从类型注册器中获取,如果没有则会使用类加载器根据全限定类型去加载(Resource.classForName中做了这个事情)。
在这里插入图片描述
到这里为止,Mybatis配置文件中的插件的解析就完成了。

现在我们来看看Mybatis是在什么时候拦截的。

下面截图中的方法是Configuration中的4个方法。这些方法在MyBatis的一个操作(新增,删除,修改,查询)中都会被执行到,执行的先后顺序是Executor、ParameterHandler、ResultSetHandler、StatementHandler;

其中ParameterHandler和ResultSetHandler的创建是在创建StatementHandler(3个可用的实现类CallableStatementHandler、PreparedStatementHandler、SimpleStatementHandler)的时候其构造函数调用的(这3个实现类的构造函数其实都调用了父类BaseStatementHandler的构造函数)。
在这里插入图片描述
这4个方法实例化了对应的对象之后,都会调用interceptorChain的pluginAll方法,InterceptorChain的pluginAll上面已经说过了,就是遍历所有的拦截器,然后调用各个拦截器的plugin方法。注意:拦截器的plugin方法的返回值会直接被赋值给原先的对象

接下来分析一下Plugin.wrap方法的执行。

  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

getSignatureMap方法:首先会拿到拦截器这个类的@Interceptors注解,然后拿到这个注解的属性@Signature注解集合,然后遍历这个集合,遍历的时候拿出@Signature注解的type属性(Class类型),然后根据这个type得到带有method属性和args属性的Method。由于@Interceptors注解的@Signature属性是一个属性,所以最终会返回一个以type为key,value为Set的Map。

getAllInterfaces方法:根据目标实例target(这个target就是之前所说的MyBatis拦截器可以拦截的类,Executor,ParameterHandler,ResultSetHandler,StatementHandler)和它的父类们,返回signatureMap中含有target实现的接口数组。

所以Plugin类的作用就是根据 @Interceptors注解,得到这个注解的属性 @Signature数组,然后根据每个 @Signature注解的type,method,args属性使用反射找到对应的Method。最终根据调用的target对象实现的接口决定是否返回一个代理对象替代原先的target对象。

如果返回了代理类,那么代理类最终会执行Interceptor接口的interceptor方法。
在这里插入图片描述

参考

  1. 视频课程案例讲解
  2. Mybatis官网。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浪潮 KaiwuDB x 大数据中心 | 数据驱动政府治理能力快速提升

业务背景 我国工业互联网大数据资源存在孤立、分散、封闭等问题&#xff0c;数据价值未能得到有效利用&#xff0c;数据主权和数据安全面临重大威胁。 发挥数据对工业经济的基础支撑和创新引擎作用&#xff0c;可促进工业互联网的创新发展&#xff0c;加速数据驱动政府治理能…

CVPR23 | 可编辑3D场景布局的文本引导多对象合成NeRF

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2303.13843 0.背景&#xff1a; 最近&#xff0c;文本到图像生成通过将视觉-语言预训练模型与扩散模型相结合&#xff0c;取得了巨大的成功。这些突破也使得强大…

cortex A7核按键中断实验

cortex A7核按键中断实验 一、分析电路图 实验目的&#xff1a;完成板子三个按键操作 1.1 电路图IO口 KEY1------>PF9 KEY2------>PF7 KEY3------>PF8 1.2 工作原理 KEY1 ------> PF9 ------> 按键触发方式&#xff1a;下降沿触发 KEY2 ------> PF7 …

HarmonyOS元服务端云一体化开发快速入门(上)

一、前提条件 您已使用已实名认证的华为开发者帐号登录DevEco Studio。 请确保您的华为开发者帐号余额充足&#xff0c;账户欠费将导致云存储服务开通失败。 二、选择云开发模板 1.选择以下任一种方式&#xff0c;打开工程创建向导界面。 如果当前未打开任何工程&#xff0c…

Question Log

Question Log 提示&#xff1a;记录一下平常遇到的坑 Question Log&#xff08;★ &#xff1e; &#xff09; Question LogⅠ、★ &#xff1e; 使用VsCode构建Unity开发环境1.环境配置2.遇到的相关问题★.The .NET Core SDK cannot be located: A valid dotnet installation …

JS + 浮动 + 递归实现图片瀑布流懒加载

思路 页面 pege 分成左浮动的数列 lineBox&#xff0c;每列中图片 sinImg 依次向下摆放每加载一张图片时&#xff0c;判断页面中哪列的高度最小&#xff0c;将当前图片放到最小的那列尾部监听当前图片 onload 事件&#xff0c;当前图片加载完成后&#xff0c;再加载下一张图片…

力扣题库刷题笔记5--最长回文子串

1、题目如下&#xff1a; 2、个人Python代码实现&#xff1a; 首先想到的是通过类似冒泡排序的方式进行切片&#xff0c;然后判断切片的子字符串是否为回文字符串&#xff0c;然后记录出最长的回文字符串&#xff0c;代码如下&#xff1a; 可以看到&#xff0c;通过切片的方式&…

剑指offer35 复杂链表的复制

复杂链表的复制 文章目录 复杂链表的复制方法一 回溯哈希表第二种解释 方法二&#xff1a;拼接拆分算法流程 参考文献 本题要求我们对一个复杂链表进行复制。在复杂链表中&#xff0c;每个节点除了有一个next指针指向下一个节点&#xff0c;还有一个random指针指向链表中的任意…

嵌入式容器源码解析

问题分析 不同于使用springmvc,在我们使用springboot时无需配置tomcat就可以直接使用&#xff0c;这就说明springboot已经在我们启动项目时将tomcat配置好了&#xff0c;接下来我们就来看看springboot底层是怎么实现的。 源码解析 ServletWebServerFactoryAutoConfiguratio…

Python 标准库 - 并发执行

Python 标准库 - 并发执行 1. 简单介绍2. 程序示例2.1 threading 编程示例2.2 multiprocessing 编程示例2.3 concurrent.futures 编程示例 1. 简单介绍 Python 标准库 非常庞大&#xff0c;所提供的组件涉及范围十分广泛&#xff0c;官方参考链接https://docs.python.org/zh-cn…

unity 3d实现下雨、雾气、萤火虫和火花四溅的粒子效果

文章目录 先看最终效果1. 下雨效果2. 雾气效果3. 萤火虫和火花四溅的效果 3d下雨粒子效果涟漪效果雨滴和涟漪效果结合水花效果雨滴涟漪水花结合问题雾气效果萤火虫火花效果萤火虫和火花效果结合完毕 先看最终效果 1. 下雨效果 2. 雾气效果 3. 萤火虫和火花四溅的效果 3d下雨粒…

函数栈帧的创建与销毁

函数栈帧的创建与销毁 前言认识相关寄存器认识相关汇编命令详解思路图 前言 函数栈帧的创建与销毁在不同编译器下&#xff0c;函数调用过程中栈帧的创建略有差异&#xff0c;具体细节取决于编译器的实现&#xff0c;但大体逻辑是一致的。&#xff08;在使用编译器时&#xff0…

某游戏登录密码加密,webpack

注意&#xff1a;文章内容仅用于学习和技术交流&#xff0c;切勿做出违法的事情&#xff0c;如有侵权请联系我删除。 网址&#xff08;今天的大冤种&#xff09;&#xff1a;aHR0cHM6Ly93d3cuZ205OS5jb20v 一&#xff0c;分析 从上面图片可以看到&#xff0c;他的密码是加密了…

桥接模式(十)

不管怎么样&#xff0c;都要继续充满着希望 上一章简单介绍了适配器模式(九), 如果没有看过, 请观看上一章 一. 桥接模式 引用 菜鸟教程里面的 桥接模式介绍: https://www.runoob.com/design-pattern/bridge-pattern.html 桥接&#xff08;Bridge&#xff09;是用于把抽象化…

谷粒商城p46-配置网关路由与路径重写

软件 &#xff1a; vscode idea 服务&#xff1a; renren-fast&#xff0c;gulimall-product&#xff0c;gulimall-gateway、nacos 前提条件&#xff1a; gateway、renren-fast已经注册到nacos 注意&#xff1a; 1、renren-fast单独注入nacos依赖&#xff0c;不要注入common…

#2023开放原子全球开源峰会之旅

#2023我在开源峰会 2023开放原子全球开源峰会参会指南 嗨咯&#xff0c;大家好&#xff01; 6月11号&#xff0c;是一年一度的开放原子大会&#xff0c;有幸参加&#xff0c;很开心&#xff01; 文章目录 1、逛展区&#xff08;领周边&#xff09;环节1.1 CSDN展区1.2 阿里云 …

ansible的部署和命令模块

一、 ansible 的概述 1、ansible简介 Ansible是一款为类Unix系统开发的自由开源的配置和自动化工具。 它用Python写成&#xff0c;类似于saltstack和Puppet&#xff0c;但是有一个不同和优点是我们不需要在节点中安装任何客户端。 它使用SSH来和节点进行通信。Ansible基于 …

(九)CSharp-数组

一、矩形数组 1、访问数组元素 class Program{static void Main(string[] args){int[] intArr1 new int[15];intArr1[2] 10;int var1 intArr1[2];int[,] intArr2 new int[5, 10];intArr2[2, 3] 7;int var2 intArr2[2, 3];int[] myIntArray new int[4];for (int i 0; i…

计算字母出现次数【存在括号计算】

计算字母出现次数【存在括号计算】 此代码考虑到了本问题的大多可能情况&#xff0c;闲话少述&#xff0c;代码中的注释很丰富。 代码绝对可以解决你的问题&#xff01; 不行你就评论&#xff0c;回复速度超快 作者时间YaoChongChong2023年6月14日10&#xff1a;40 Descript…

【gcc, cmake, eigen, opencv,ubuntu】五.CMakeLists.txt编写

文章目录 CMakeLists.txt编写1.CMakeLists.txt模板2.设置编程语言版本3.设置编译类型Debug&#xff0c;Release4.设置获取文件列表5.添加include目录6.配置编译选项 CMakeLists.txt编写 1.CMakeLists.txt模板 一个使用opencv 的 CMakeLists.txt # cmake最低版本要求 cmake_m…