Mybatis学习之插件

news2025/2/4 10:34:47

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官网。

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

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

相关文章

【C/C++】引用()的概念和用法

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

推荐几款可以大幅提高开发效率的vscode插件

1、Vue 2 Snippets 这是一款基于vue2的代码片段提示插件&#xff0c;特别好用&#xff0c;可以有效的提高我们的编码速度。 他的能力非常强大&#xff0c;具体还需要我们去看他的文档文档地址 &#xff0c;解锁更多能力。 2、Path Autocomplete 这是一款路劲提示插件 vscod…

计算机网络实验第一课:HTTP和DNS

实验准备 1.先安装好wireshark 2.开启抓包&#xff0c;然后用过滤器选中http。 3.输入http://gaia.cs.umass.edu 网址&#xff0c;摁下回车键。&#xff08;这里我做实验的时候出了点意外&#xff0c;我一开始开着梯子&#xff0c;然后一直都是通过代理访问的网页&#xff…

Spring Security Oauth2.1 最新版 1.1.0 整合 (基于 springboot 3.1.0)gateway 完成授权认证

目录 背景 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0官方文档 基础 spring security OAuth2.0 模块构成 授权方式 集成过程 官方demo 代码集成 依赖 授权服务AuthorizationServerConfig配置 重要组件 测试 查看授权服务配置 访问授权服务 授…

简单实用的快递查询工具,支持批量查询

大家如果遇到快递一直没到的情况&#xff0c;是不是会很心急呢&#xff1f;这时候就要去查询一下快递具体的运输轨迹了&#xff0c;看看自己的快递运输到哪里了。今天小编给大家推荐一款快递信息查询软件&#xff1a;“固乔快递查询助手”&#xff0c;这款软件能够自动识别单号…

管理类联考——英语——知识篇——考研英语熟词生义

考研英语熟词生义 A 1.abandon【生义】放松&#xff0c;沉溺 2.abide【【生义】容忍&#xff0c;经受住 3.abroad【生义】广为流传&#xff0c;传开 4.abrupt【生义】陡峭的&#xff0c;险峻的 5.absent【生义】漫不经心的 6.absolute【【生义】基本的 7.absorb【生义】…

Redis 发布和订阅详细介绍

发布和订阅 发布和订阅是什么 一句话&#xff1a;Redis 发布订阅(pub/sub) 是一种消息通信模式&#xff1a;发送者(pub) 发送消息&#xff0c;订阅者(sub) 接收消息 Redis 客户端可以订阅任意数量的频道 一图胜千言 1、客户端订阅频道示意图 2、当给这个频道发布消息后&am…

[CKA]考试之网络策略NetworkPolicy

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 在现有的namespace fubar中创建一个名为allow-port-from-namespace的新Netwo…

云原生时代崛起的编程语言Go远程调用gRPC实战

文章目录 概述定义背景特点四种服务方法 实战环境配置proto文件简单RPCToken认证服务器流式RPC客户端流式RPC双向流式RPC 概述 定义 gRPC 官网地址 https://grpc.io/ 源码release最新版本v1.55.1 gRPC 官网文档地址 https://grpc.io/docs/ gRPC 源码地址 https://github.com/g…

【vue3】13-前端路由-Vue-Router的详解: 从入门到掌握

前端路由 前端路由的发展历程认识前端路由&#xff1a;后端路由阶段前后端分离阶段 Vue-Router的使用vue-router的基本使用vue-router的使用过程基本使用的细节补充 路由懒加载分包处理路由的其他属性动态路由路由嵌套路由的编程式导航动态管理路由对象路由导航守卫钩子 前端路…

【毕业N年系列】 毕业第五年

距离上一篇 【毕业N年系列】已经过去一年时间了。没想到自己当初的一个简单的想法竟然能坚持到第五年&#xff0c;希望自己能坚持下去吧&#xff0c;在耄耋古稀之年依然能回看自己的人生。当然本来想着今天能早点下班回来写这篇文章&#xff0c;显然又没有实现&#xff0c;加班…

termux安卓手机 无线adb调试安卓手机

调试机安转termux app Apk下载 安装 https://f-droid.org/packages/com.termux 打开termux 挂载内部存储到~/storage 目录 $ temux-setup-storage 换源加快下载与更新速度 termux-change-repo 选 Main 然后 清华 下载基础软件 pkg install git vim perl nodejs-lts wget…

竞赛无人机搭积木式编程(三)---用户自定义航点自动飞行功能(全局定位,指哪打哪)

竞赛无人机搭积木式编程&#xff08;三&#xff09;---用户自定义航点自动飞行功能&#xff08;全局定位&#xff0c;指哪打哪&#xff09; 无名小哥 2023年6月10日 用户通过对前面两讲中全国大学生电子设计竞赛真题植保无人机&#xff08;2021&#xff09;、送货无人机&#…

新人如何学习性能测试?一文5个步骤带你入门性能测试!

写在前面&#xff1a;测试岗位细化后分为很多&#xff0c;功能测试、性能测试、自动化测试、测试开发&#xff0c;这些都叫测试。性能测试在所有测试分工中&#xff0c;地位和薪资都比较高&#xff0c;可见下方图片&#xff1a; 中级第3条&#xff0c;高级第1条&#xff0c;都是…

深度学习入门——感知机

1 感知机是什么 感知机接收多个输入信号&#xff0c;输出一个信号。这里所说的“信号”可以想象成电流或河流那样具备“流动性”的东西。像电流流过导线&#xff0c;向前方输送电子一样&#xff0c;感知机的信号也会形成流&#xff0c;向前方输送信息。但是&#xff0c;和实际…

Java集合-Collection Map

文章目录 概念IterableCollection接口List接口ArrayListVectorLinkedListArrayList 和 LinkedList 比较 SetHashSet LinkedHashSetMapHashMap 概念 1.集合主要是两组:单列集合(Collection) , 双列集合(Map) 2.Collection 接口有两个重要的子接口 List ,Set . 他们的实现子类都…

SWIG介绍和使用

官网&#xff1a;https://www.swig.org/ github&#xff1a;https://github.com/swig SWIG 是一种软件开发工具&#xff0c;可将用 C 和 C 编写的程序与各种高级编程语言连接起来。 SWIG 与不同类型的目标语言一起使用&#xff0c;包括常见的脚本语言&#xff0c;如 Javascri…

redis集群-Redis(六)

上篇文章介绍了哨兵架构以及集群搭建。 哨兵架构&redisCluster-Redis&#xff08;五&#xff09;https://blog.csdn.net/ke1ying/article/details/131178880 redisCluster 接着上篇文章继续讲解&#xff0c;前面一个linux服务器已经有了8001和8004节点&#xff0c;现在继…

八大典型的黑盒测试方法已来袭,快快接住!

前言 有了软件缺陷的暴露&#xff0c;我们就需要通过各种软件测试的方法来查找出软件的漏洞&#xff0c;编写出测试用例&#xff0c;及时修改bug。 在下面的这篇文章中&#xff0c;我们将谈论八大典型的黑盒测试方法&#xff0c;一起来学习⑧&#x1f4a1; 一、等价类划分法…

【Python实战】Python采集王者皮肤图片

前言 我们上一篇介绍了&#xff0c;如何采集王者最低战力&#xff0c;本文就来给大家介绍如何采集王者皮肤&#xff0c;买不起皮肤&#xff0c;当个桌面壁纸挺好的。下面&#xff0c;我和大家介绍如何获取数据。 环境使用 python 3.9pycharm 模块使用 requests 模块介绍 re…