MyBatis是纸老虎吗?(四)

在《MyBatis是纸老虎吗?(三)》这篇文章中我们一起梳理了MyBatis配置文件的解析流程,并详细介绍了其中的一些常见节点的解析步骤。通过梳理,我们弄清楚了MyBatis配置文件中的一些常用配置项与Java Bean之间的对应关系,这进一步加深了我们对MyBatis配置文件认识;通过梳理,我们对MyBatis的使用步骤有了更全面的了解,这进一步提高了我们使用MyBatis的能力。今天我想继续梳理MyBatis这个框架,因为我们了解的,仅仅是冰山的一角。MyBatis中还有很多其他实用的知识点和好的设计思想值得我们深究。那今天就一起研究一下MyBatis配置文件中的plugins元素吧。

1 plugins元素的定义及解析

上篇文章——《MyBatis是纸老虎吗?(三)》——有提到过这个元素。这个元素的作用就是允许开发者指定一个插件,这个插件可以在映射语句执行过程中的某一点进行拦截,然后做一些特殊的处理,比如数据分页、操作日志增强、sql性能监控等。那如何定义一个插件呢?很简单,只需实现org.apache.ibatis.plugin.Interceptor接口即可。下面是一个自定义插件的示例:

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class ExamplePlugin implements Interceptor {private Properties properties = new Properties();public Object intercept(Invocation invocation) throws Throwable {// implement pre processing if needObject returnObject = invocation.proceed();// implement post processing if needreturn returnObject;}public void setProperties(Properties properties) {this.properties = properties;}}

这个拦截器中的intercept()方法并未做任何处理,只是调用了Invocation对象上的proceed()方法,并将该方法的执行结果返回给上级调用者。梳理到这里,我想看一下Interceptor接口的源码,如下所示:

public interface Interceptor {Object intercept(Invocation invocation) throws Throwable;// default xxxx,如果没有没记错的话,这是 jdk1.8的新特性default Object plugin(Object target) {return Plugin.wrap(target, this);}default void setProperties(Properties properties) {// NOP}
}

由此源码,我们可以知道Interceptor,拦截器,是一个接口,其中仅有一个名为intercept的方法,因此实现该接口的类一般都要对这个方法进行实现。上面展示的自定义拦截器就对这个方法进行了实现。注意:这个方法会接收一个Invocation类型的参数,该类的源码如下所示:

public class Invocation {private final Object target;private final Method method;private final Object[] args;public Invocation(Object target, Method method, Object[] args) {this.target = target;this.method = method;this.args = args;}public Object getTarget() {return target;}public Method getMethod() {return method;}public Object[] getArgs() {return args;}public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args);}}

梳理到这里,我非常想知道是这个自定义拦截器要怎样用。想必诸位都已先我一步知道了这个问题的答案:直接在MyBatis的配置文件config.xml中新增plugins配置项。具体代码如下所示:

<plugins><plugin interceptor="包名.插件类名"></plugin>
</plugins>

进行到这里,所有的前期准备工作就完成了。下面就一起看一下这个元素的解析过程吧!通过上篇文章我们知道XMLConfigBuilder类的parse()方法开启了执行流程,其中执行解析工作的核心是与其同属一类的parseConfiguration()方法。该方法会对MyBatis配置文件中的元素按照既定顺序逐个解析。这些元素的解析顺序为:properties、settings、typeAliases、plugins、objectFactory、objectWrapperFactory、reflectorFactory、environments、databaseIdProvider、typeHandlers、mappers。上节我们一起梳理了properties、settings、typeAliases、environments四个元素的解析过程,这节就详细梳理一下plugins元素的解析过程。解析plugins元素的方法的名为pluginsElement(),其源码如下所示:

private void pluginsElement(XNode context) throws Exception {if (context != null) {for (XNode child : context.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}
}

该方法被调用时的状态如下图所示(注意截图中context参数的内容,就是MyBatis配置文件中的plugins标签下的内容):

借助这段运行时状态图,让我们一起分析一下这段代码的执行逻辑:1.拿到context参数所代表的plugins元素下的所有plugin节点,并遍历这些节点,然后执行下述步骤;2.拿到plugin节点上interceptor属性的值,这个值就是我们自定义的拦截器的包全名+类名;3.解析plugin元素下的子元素,并将其包装为Properties对象;4.解析第二步拿到的数据,然后加载相应的类,并实例化一个对象出来(注意:这里用到了反射);5.将第三步解析出来的Properties对象设置到第四步创建的Interceptor对象上;6.将第四步创建的Interceptor对象设置到Configuration对象的interceptorChain属性上(这个操作是通过调用Configuration类上的addInterceptor()方法完成的。这里还有一点需要注意:interceptorChain的类型为InterceptorChain,这让我想到了责任链及Spring的AOP和事务,有兴趣的可以翻看一下我之前梳理的与这两个主题有关的系列文章《Spring AOP系列》、《Spring事务系列》)。

关于上述解析步骤,个人觉得有以下几点需要注意:

  1. 上述第二步和第五步中都提到了Properties,为什么我们可以在plugin元素中使用property标签呢?为什么我们可以将这些值设置到Interceptor类型的对象上呢?这两个问题很好回答。关于第一个问题:因为MyBatis支持,如若不然,plugins元素的解析逻辑中不会出现Properties properties = child.getChildrenAsProperties()这样一行代码。那MyBatis是怎么支持的呢?这个就要看MyBatis配置文件的dtd约束文件了,先看下面这段从mybatis-3-config.dtd文件中摘抄出来的代码:<!ELEMENT plugin (property*)>。这段代码的大致意思就是说在plugin元素下可以有零个或多个property标签(具体参照下图“mybatis dtd文件关于plugin的定义”)。关于第二个问题:根据前面列出的源码,不难发现Interceptor源码中有一个default修饰的setProperties()方法,该方法返回值为void类型,默认不做任何处理。前面自定义的拦截器实现了这个方法。正因为Interceptor中有这样一个方法,所以解析代码中才有这样一句:interceptorInstance.setProperties(properties)。(也就是上面描述中的第五步)
  2. 上述第二步调用XMLConfigBuilder类的父类BaseBuilder类resolveClass()方法去解析我们在plugin元素中指定的interceptor属性值(包全名+拦截器名),该方法会继续调用BaseBuilder类中的resolveAlias()方法,这个方法会继续调用TypeAliasRegistry对象的resolveAlias()方法,这个方法的源码在上篇文章中已经展示过,这里就不再啰嗦,有兴趣可以翻看源码或者翻阅上篇文章。这段代码会直接将传递进来的string参数转为小写,然后从typeAliases中查找这个string参数代表的key是否存在,如果存在,则直接返回其对应的Class<?>类型的值,如果不存在,则直接使用Resources加载这个类。plugins的解析最终走的就是这一步

mybatis dtd文件关于plugin的定义

2 关于InterceptorChain的介绍

上小节的第六步中提到解析出来的Interceptor的对象会被设置到Configuration对象中的interceptorChain属性上。这个属性的实际类型为InterceptorChain。该类的源码为:

public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}
}

这个类中定义了一个List类型的变量interceptors,其持有的类型为Interceptor,所以第六步调用Configuration类上的addInterceptor()方法,最终实际上调用的就是这个类上的addInterceptor()方法向interceptors变量中添加数据。这里面的pluginAll()方法是最终调度的入口

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

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

相关文章

交叉注意力融合时域、频域特征的FFT + CNN -BiLSTM-CrossAttention电能质量扰动识别模型

往期精彩内容&#xff1a; 电能质量扰动信号数据介绍与分类-Python实现-CSDN博客 Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(二)基于CNN模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(三)基于Transformer…

AI浸入社交领域,泛娱乐APP如何抓住新风口?

2023年是大模型技术蓬勃发展的一年&#xff0c;自ChatGPT以惊艳姿态亮相以来&#xff0c;同年年底多模态大模型技术在国内及全球范围内的全面爆发&#xff0c;即模型能够理解并生成包括文本、图像、视频、音频等多种类型的内容。例如&#xff0c;基于大模型的文本到图像生成工具…

实战纪实 | 从接口信息泄露到RCE

打点 开局一个登录框 信息收集 发现了一处接口泄露了部分信息 不过只有支付宝密钥的信息无法扩大危害&#xff0c;此时尝试寻找了一下其他同类型系统同样的接口&#xff0c;查看一下是否泄露的信息相同 因为如果相同就说明是静态的&#xff0c;没有价值 横向收集 此时访问其…

后端系统开发之——接口参数校验

今天难得双更&#xff0c;大家点个关注捧个场 原文地址&#xff1a;后端系统开发之——接口参数校验 - Pleasure的博客 下面是正文内容&#xff1a; 前言 在上一篇文章中提到了接口的开发&#xff0c;虽然是完成了&#xff0c;但还是缺少一些细节——传入参数的校验。 即用户…

JMeter 并发测试和持续性压测详解

并发测试和持续性压测都是评估系统性能的常用方法&#xff0c;它们可以帮助开发人员发现并解决系统中的性能问题。本文来详细介绍下。 概念 并发测试&#xff1a; 旨在评估系统在同时处理多个用户请求时的性能。在这种 测试 中&#xff0c;系统会暴露于一定数量的用户负载下&…

【双指针】算法例题

目录 二、双指针 25. 验证回文数 ① 26. 判断子序列 ① 27. 两数之和II - 输入有序数组 ② 28. 盛最多水的容器 ② 29. 三数之和 ② 二、双指针 25. 验证回文数 ① 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一…

行业回暖?这个行业岗位需求飙升6倍!程序员们提前恭喜了!

前言 随着今年史上最长春节假期正式收官&#xff0c;各行各业相继进入开工节奏&#xff0c;就业市场开启持续升温模式。 今年开工首周&#xff0c;人才需求增长明显&#xff0c;求职者活跃度大大增多&#xff0c;就业市场进入了繁忙有序的节奏&#xff0c;呈现出春招市场的勃…

QT网络编程之获取本机网络信息

一.概述 查询一个主机的MAC地址或者IP地址是网络应用中常用到的功能&#xff0c;Qt提供了QHostInfo和QNetworkInterface 类可以用于此类信息的查询 1.QHostInfo 类&#xff08;显示和查找本地的信息&#xff09; 2.QNetworkInterface 类&#xff08;获得应用程序上所在主机的…

在window命令行中,使用conda创建新的虚拟环境时出现的问题以及解决办法

一.出现的问题 C:\Users\zhu>conda create -n ML python3.8 Collecting package metadata (current_repodata.json): failedCondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/win-64/current_repodata.js…

南京大学AI考研,宣布改考408!

官网还没通知 附上南大与同层次学校近四年的分数线对比&#xff0c;整体很难 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 如果确定要冲南大的话建议提早调整自己的复习路线&…

Combining external-latent attention for medical image segmentation

结合外部潜在注意的医学图像分割 摘要 注意机制是提高医学图像分割性能的新切入点。如何合理分配权重是注意力机制的关键,目前流行的方法包括全局压缩和使用自注意操作的非局部信息交互。然而,这些方法过于关注外部特征,缺乏对潜在特征的开发。全局压缩方法通过全局均值或…

介绍5款 世界范围内比较广的 5款 mysql Database Management Tool

介绍5款 世界范围内比较广的 5款 Mysql Database Management Tool 文章目录 介绍5款 世界范围内比较广的 5款 Mysql Database Management Tool前言MySQL Workbench&#xff1a;Navicat Premium&#xff1a;DBeaver Community&#xff1a;HeidiSQL&#xff1a;SQLyog&#xff1a…

如何解决node-sass下载用的还是过期的淘宝源?

下载node-sass发现报错过期的证书 把npm的淘宝源换成最新的https://registry.npmmirror.com后发现还是指向了以前的淘宝源&#xff0c;看到一位博主说&#xff0c;单改npm源不够还要改下载node-sass的源&#xff0c;再次搜索另外一位博主提供了命令npm config ls可以使用它来查…

文件包含漏洞之包含NGINX日志文件(常用)

条件&#xff1a;知道目标服务器的日志文件存贮路径&#xff0c;并且存在文件包含漏洞 首先对目标服务器发送一次含有木马的请求&#xff0c;目的是让目标服务器日志中生成含有木马的日志记录。因为发送过程中&#xff0c;使用了url编码&#xff0c;我们抓包进行更改成能够执行…

图文详解io_uring高性能异步IO架构

说到高性能网络编程&#xff0c;我们第一时间想到的是epoll机制&#xff0c;epoll很长一段时间统治着整个网络编程江湖&#xff0c;然而io_uring的出现&#xff0c;似乎在撼动epoll的统治地位&#xff0c;今天我们来揭开io_uring的神秘面纱。 1.io_uring简介 io_uring是一个L…

竞赛 - 基于机器视觉的图像拼接算法

前言 图像拼接在实际的应用场景很广&#xff0c;比如无人机航拍&#xff0c;遥感图像等等&#xff0c;图像拼接是进一步做图像理解基础步骤&#xff0c;拼接效果的好坏直接影响接下来的工作&#xff0c;所以一个好的图像拼接算法非常重要。 再举一个身边的例子吧&#xff0c;…

express+mysql+vue,从零搭建一个商城管理系统16--收货地址(全国省市县名称和code列表)

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、新建config/area.js二、新建models/address.js三、新建dao/address.js四、新建routes/address.js五、添加地址六、查询用户地址列表总结 前言 需求&#xff1a;主要学习express&#xff0c;所以先写serv…

浅谈大模型“幻觉”问题

大模型的幻觉大概来源于算法对于数据处理的混乱&#xff0c;它不像人类一样可以by the book&#xff0c;它没有一个权威的对照数据源。 什么是大模型幻觉 大模型的幻觉&#xff08;Hallucination&#xff09;是指当人工智能模型生成的内容与提供的源内容不符或没有意义的现象。…

SpringCloud Gateway工作流程

Spring Cloud Gateway的工作流程 具体的流程&#xff1a; 用户发送请求到网关 请求断言&#xff0c;用户请求到达网关后&#xff0c;由Gateway Handler Mapping&#xff08;网关处理器映射&#xff09;进行Predicates&#xff08;断言&#xff09;&#xff0c;看一下哪一个符合…

windows docker

写在前面的废话 最近在学习riscv的软件相关内容&#xff0c;倒是有别人的sg2042机器可以通过ssh使用&#xff0c;但是用起来太不方便了&#xff0c;经常断掉&#xff0c;所以想着在自己的机器上跑一跑riscv的操作系统。最常见的有两种方法吧&#xff0c;第一个就是qemu&#xf…