PageHelper导致自定义Mybatis拦截器不生效

2023-11-12

背景:

最近由于公司要做统一的数据变更记录,以前是基于Aop来做的,这样效率很低,而且在做批量处理(insert,update,delete)操作时基本不可用。所以我打算使用CDC(如Canal,Maxwell等工具)来监听mysql的binlog来做。但是不是所有的表都会有user_id字段,所以我们须要在sql上做一些处理,因为公司现在统一用的是mybatis,那么现在我觉得比较好的方式就是在mybatis上进行拦截改造sql.将userId从应用层获取到并写入到须要执行的sql上(只对insert,update,delete记录)。
如:有如下sql: update table set a= 1 where name =3
改造的结果就是:/** userId:1,traceId:123456**/ update table set a= 1 where name =3
这样我们就可以记录一次操作改了哪些数据,改数据的人是哪个。

开始干:

这里面有几个技术点,且都不怎么复杂,今天我们只聊mybatis拦截器。其实写一个拦截器还是很简单的,网上有很多的代码。代码写完后,突然发现有些项目的自定义mybatis拦截器没有生效。于是就开始google研究了一下,发现是因为我们这些不生效的项目使用了PageHelper.于是找了一些大神的解决方案,和拦截器的顺序有关。先说一下结论:
MyBatis的拦截器采用责任链设计模式,多个拦截器之间的责任链是通过动态代理组织的。我们一般都会在拦截器中的intercept方法中往往会有invocation.proceed()语句,其作用是将拦截器责任链向后传递,本质上便是动态代理的invoke

PageHelper在intercept方法中执行完后没有执行invocation.proceed(),意味着这玩意儿没有继续传递责任链(可能他有自己的想法)。所以他就没有进入我们自己的拦截器。

注意,敲黑板:

A.不是所有的拦截器都必须要指定先后顺序。
拦截器的调用顺序分为两大种,第一种是拦截的不同对象,例如拦截 Executor 和 拦截 StatementHandler 就属于不同的拦截对象, 这两类的拦截器在整体执行的逻辑上是不同的,在 Executor 中的 query 方法执行过程中会调用StatementHandler。

所以StatementHandler 属于 Executor 执行过程中的一个子过程。 所以这两种不同类别的插件在配置时,一定是先执行 Executor 的拦截器,然后才会轮到 StatementHandler。所以这种情况下配置拦截器的顺序就不重要了,在 MyBatis 逻辑上就已经控制了先后顺序。
所以如果你一个是Executor 类型的拦截器,一个是StatementHandler类型的拦截器,你可以不用管他顺序,也就是说你只须要定义好类型都Executor的拦截器顺序。

B.类型都为Executor的拦截器顺序问题:
如果你的拦截器定义的顺序是这样的(你可以通过获取sqlSessionFactory.getConfiguration()去查看里面的InterceptorChain然后看到各个interceptor的顺序):
<plugins>
<plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor1"/>
<plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor2"/>
<plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor3"/>
</plugins>
他执行的顺序不是先执行1,2,3,而执行的顺序是3,2,1。

 

Interceptor3:{
    Interceptor2: {
        Interceptor1: {
            target: Executor
        }
    }
}

从这个结构应该就很容易能看出来,将来执行的时候肯定是按照 3>2>1>Executor>1>2>3 的顺序去执行的。 可能有些人不知道为什么3>2>1>Executor之后会有1>2>3,这是因为使用代理时,调用完代理方法后,还能继续进行其他处理。处理结束后,将代理方法的返回值继续往外返回即可。

C(解决方案).因为PageHelper是Excetor类型的拦截器,所以按照前面两条的理论,我们如果想要在PageHelper拦截器前面执行,就必须要将我们自己的拦截器添加到他的拦截器后面。

那该怎么做呢?
我们可以通过这种方式来做:

我们去看PageHelperAutoConfiguration的代码是不是发现,该类上面有一个@AutoConfigureAfter(MybatisAutoConfiguration.class)注解,这表明他是在MybatisAutoConfiguration加载完成后,才执行自己的加载。那么我们是不是可以也可以构建一个类似的代码呢,虽然我们不是一个starter,但是我们可以通过这种操作来实现我们的须求。

(1)在src/main/resources/META-INF目录下面,创建一个spring.factories的文件
(2)spring.factories里的内容是:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.llyt.exculd.TestLogAutoConfiguration
这个com.llyt.exculd.TestLogAutoConfiguration,就是你自己的配置类的全路径。该类的代码在后面。
(3) TestLogAutoConfiguration代码:

 

@Configuration
@AutoConfigureAfter(PageHelperAutoConfiguration.class)
public class TestLogAutoConfiguration {
    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @PostConstruct
    public void addMyInterceptor() {
        ExampleOnePlugin e = new ExampleOnePlugin();
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            sqlSessionFactory.getConfiguration().addInterceptor(e);
        }
    }
}

至此,这种方法就OK了。但是你可能会执行不成功,该类的addMyInterceptor方法总是先于PageHelperAutoConfiguration的addPageInterceptor()方法执行,这就意味着你的拦截器总是添加到在pageHelper拦截前面的,那么他总是在PageHelper拦截器后面执行。
如果出现这种情况,说明你可能在spring boot主类上配置了
@ComponentScan("****"),且该类会被这个扫描到,这个就是导致的原因所在。

这里面有一个知识点就是,不是配置了@AutoConfigureAfter(A.class)就一定表示该类一定在A类后面执行。

如果配置类在 spring.factories 中配置了且而如果你的类被自己 Spring Boot 启动类扫描到了,那么该类会被会优先扫描到,配置类对顺序有要求时就会出错。

那么该怎么解决呢?

解决的方法有两个:

a.使用骚操作。

如果你将自己的配置类放到特别的包下,不使用 Spring Boot 启动类扫描。完全通过 spring.factories 读取配置就可以实现这个目的。
比如,你@ComponentScan扫描的包是com.bb.cc,那么你就将该配置类放在com.bb.dd包下面。

b.如果你觉得上面这种不习惯,可以用使用excludeFilters :

 

@ComponentScan(basePackages = {"com.llyt"},  excludeFilters = @ComponentScan.Filter(
     type = FilterType.REGEX,
     pattern = "com.llyt.exculd.*"))

将你的配置类放在com.llyt.exculd包下面就行了。

至此,mybatis拦截器的不生效的问题,搞完了。

参考文献:
http://xtong.tech/2018/08/01/MyBatis%E6%8B%A6%E6%88%AA%E5%99%A8%E5%9B%A0pagehelper%E8%80%8C%E5%A4%B1%E6%95%88%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/Interceptor.md


作者:water_lang
链接:https://www.jianshu.com/p/8dc9f8a4cce9
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PageHelper导致自定义Mybatis拦截器不生效 的相关文章

随机推荐

  • 怎么能学会做买卖步骤是什么(想做买卖赚钱应该先从什么做起)

    如何能学会做交易 办法是什么 胜利者 有本人的战略 遵照本人的战略 比方价格入股 趋向盯梢入股 套利 缠论买卖体例之类 波折者 自我买卖 情结买卖 动静炒买炒卖股票 贪嗔痴慢疑主宰你的操纵 你必需输得起 没有人 会在看了一本简略五官科手术的
  • JetBrains IDEA 系列产品通用xx方法(license server)

    若资金允许 请点击https www jetbrains com idea buy 购买正版 谢谢合作 学生凭学生证可免费申请正版授权 创业公司可5折购买正版授权 本文适用于所有 JetBrains 系列产品 方法一 最新方法 Jet Br
  • springboot+cache缓存上

    cache内容 缓存的意义 将方法的运行结果进行缓存 以后再要相同的数据 直接从缓存中获取 不用调用方法 cacheManager管理多个Cache组件 对缓存的真正操作CRUD操作在Cache组件中 每一个缓存组件有自己唯一一个名字 几个
  • FreeRTOS入门(07):流缓冲区 & 消息缓冲区

    文章目录 目的 基础说明 流缓冲区 相关函数 使用演示 消息缓冲区 相关函数 使用演示 总结 目的 缓冲区是操作系统中常见的一种用于任务间数据传递的机制 这篇文章将对FreeRTOS中相关内容做个介绍 本文代码测试环境见前面的文章 Free
  • 百度云BOS云存储的图片如何在访问时,同时进行格式转换、缩放等处理

    前言 之前做了一个图片格式转换和压缩的服务 结果太占内存 后来查到在访问图片链接时 支持进行图片压缩和格式转换 本来想着先格式转换 压缩图片再上传到BOS 现在变成了上传后 访问时进行压缩和格式转换 想了想 因为主要目的是提高用户的图片访问
  • vue2中使用vue-quill-editor实现编辑器

    1 参考文档 开发文档 https github com surmon china vue quill editor 例子 vue quill editor Homepage Surmon s projects 中文开发文档 前言 Quil
  • [机器学习 01] 回归算法-sklearn

    机器学习 回归 1 线性回归 有一组数据 x y 找出一个线性方程 使得数据到线上的距离总和最小 距离最小化 梯度下降法 最小二乘法 对于普通最小二乘的系数估计问题 其依赖于模型各项的相互独立性 当各项是相关的 且设计矩阵的各列近似线性相关
  • syntaxerror: 'return' outside function 解决办法

    出现syntaxerror return outside function是由于python对格式要求严格的原因 多半问题是由于格式对齐导致的 根据报错提示到相应的 py文件的相应行修改缩进 只需要保持缩进为1或着2个tab即可 修改后保存
  • 01.Vite 的安装和使用

    学习要点 1 Vite 简介 2 Vite 安装 本节课我们来开始了解 Vue 官方的构建工具 Vite 以及安装方式 一 Vite 简介 1 Vite 是官方从 Vue3 开始的全新的前端构建工具 官网如下 https vitejs de
  • 2021-11-18 Android APP安装后不在桌面显示图标的一个方法

    一 有时候安装一个app 不想在桌面上显示图标 而是在其他地方调出这个程序 要想在桌面不显示图标 只需注释掉APP入口Activity中的
  • EC600 QuecPython开发环境搭建、固件下载,最方便的OpenCPU物联网4G通信解决方案

    EC600 QuecPython 官方资源汇总 开发环境搭建 1 安装windows驱动 2 验证模组的固件版本 3 烧录QuecPython固件 小试QuecPython 1 查看系统信息 2 点亮V1 2开发板上的LED D6 3 UA
  • HIT SC Lab1 小小的总结(主要是工具)

    Fine 自动机与软构实验轮番轰炸的一周 在自动机考完的晚上 写下这篇有关实验一的思考总结 Git的使用 软构这门课提交实验的方式与以前的实验不一样 是使用github上传 因此git的使用就成了刚需 接下来讲一下本次实验中发现的几种上传方
  • Microsoft Office 2016 VOL版下载

    链接都是VOL版 和零售版功能是一样的 只是激活方便一些 三个下载链接 第一个是Office就是包含了Word Excel PPT那些的 另外两个一个是Visio 一个是Project 如果不需要的话 只下载第一个就可以了 激活方法在下面
  • Ubuntu操作系统如何截图

    Ubuntu操作系统如何截图 一 截图并保存到文件夹 1 截取全屏 2 矩形选框截取感兴趣区区域 3 截取当前活动的窗口 二 截图到剪切板 不保存进文件夹 1 截取全屏 2 矩形选框截取感兴趣区区域 3 截取当前活动的窗口 Ubuntu系统
  • 记一次java heap space的解决办法

    记一次java heap space的解决办法 java lang OutOfMemoryError java heap space 问题缘由 后台上传excel导入到数据库 数据量太大 导致报错 解决方案 用jdk自带的性能分析器 jco
  • Cisco交换机链路聚合配置

    文章目录 1 拓扑图 2 Sw1配置 3 Sw2配置 1 拓扑图 2 Sw1配置 进入特权模式 Switch gt en 进入全局模式 Switch conf t 更改设备名称 Switch config hostname Sw1 更改接口
  • LeetCode第142题解析

    给定一个链表 返回链表开始入环的第一个节点 如果链表无环 则返回 null 为了表示给定链表中的环 我们使用整数 pos 来表示链表尾连接到链表中的位置 索引从 0 开始 如果 pos 是 1 则在该链表中没有环 说明 不允许修改给定的链表
  • Verilog十大基本功9 (Multicycle Paths)

    来自 http blog chinaaet com coyoo p 31979 概述 Multicycle paths即多周期路径 指的是两个寄存器之间数据要经过多个时钟才能稳定的路径 一般出现于组合逻辑较大的那些路径 在实际工程中 除了乘
  • fgets函数使用详解(获取文件中的每行数据)

    读取文本中的每一行 include
  • PageHelper导致自定义Mybatis拦截器不生效

    背景 最近由于公司要做统一的数据变更记录 以前是基于Aop来做的 这样效率很低 而且在做批量处理 insert update delete 操作时基本不可用 所以我打算使用CDC 如Canal Maxwell等工具 来监听mysql的bin