在springboot中给mybatis加拦截器
拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加上我们自己逻辑
Mybatis拦截器设计的一个初衷是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。
- mybatis 自定义拦截器
- 1、实现Interceptor 接口,并添加拦截注解 @Intercepts
- 2、配置文件中添加拦截器
1、实现Interceptor 接口,并添加拦截注解 @Intercepts
- mybatis 拦截器默认可拦截的类型只有四种,
- Executor:拦截执行器的方法。
- ParameterHandler:拦截参数的处理。
- ResultHandler:拦截结果集的处理。
- StatementHandler:拦截Sql语法构建的处理。
- 对于我们的自定义拦截器必须使用 mybatis 提供的注解来指明我们要拦截的是四类中的哪一个类接口,具体规则如下:
- a:Intercepts 拦截器: 标识我的类是一个拦截器
- b:Signature 署名: 则是指明我们的拦截器需要拦截哪一个接口的哪一个方法
- type 对应四类接口中的某一个,比如是 Executor
- method 对应接口中的哪类方法,比如 Executor 的 update 方法
- args 对应接口中的哪一个方法,比如 Executor 中 query 因为重载原因,方法有多个,args 就是指明参数类型,从而确定是哪一个方法
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),@Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class})
})
public class MyInterceptor implements Interceptor {/*** 这个方法很好理解* 作用只有一个:我们不是拦截方法吗,拦截之后我们要做什么事情呢?* 这个方法里面就是我们要做的事情** 解释这个方法前,我们一定要理解方法参数 {@link Invocation} 是个什么鬼?* 1 我们知道,mybatis拦截器默认只能拦截四种类型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler* 2 不管是哪种代理,代理的目标对象就是我们要拦截对象,举例说明:* 比如我们要拦截 {@link Executor#update(MappedStatement ms, Object parameter)} 方法,* 那么 Invocation 就是这个对象,Invocation 里面有三个参数 target method args* target 就是 Executor* method 就是 update* args 就是 MappedStatement ms, Object parameter** 如果还是不能理解,我再举一个需求案例:看下面方法代码里面的需求** 该方法在运行时调用*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {/** 需求:我们需要对所有更新操作前打印查询语句的 sql 日志* 那我就可以让我们的自定义拦截器 MyInterceptor 拦截 Executor 的 update 方法,在 update 执行前打印sql日志* 比如我们拦截点是 Executor 的 update 方法 : int update(MappedStatement ms, Object parameter)** 那当我们日志打印成功之后,我们是不是还需要调用这个query方法呢,如何如调用呢?* 所以就出现了 Invocation 对象,它这个时候其实就是一个 Executor,而且 method 对应的就是 query 方法,我们* 想要调用这个方法,只需要执行 invocation.proceed()*//* 因为我拦截的就是Executor,所以我可以强转为 Executor,默认情况下,这个Executor 是个 SimpleExecutor */Executor executor = (Executor)invocation.getTarget();/** Executor 的 update 方法里面有一个参数 MappedStatement,它是包含了 sql 语句的,所以我获取这个对象* 以下是伪代码,思路:* 1 通过反射从 Executor 对象中获取 MappedStatement 对象* 2 从 MappedStatement 对象中获取 SqlSource 对象* 3 然后从 SqlSource 对象中获取获取 BoundSql 对象* 4 最后通过 BoundSql#getSql 方法获取 sql*/MappedStatement mappedStatement = ReflectUtil.getMethodField(executor, MappedStatement.class);SqlSource sqlSource = ReflectUtil.getField(mappedStatement, SqlSource.class);BoundSql boundSql = sqlSource.getBoundSql(args);String sql = boundSql.getSql();logger.info(sql);/** 现在日志已经打印,需要调用目标对象的方法完成 update 操作* 我们直接调用 invocation.proceed() 方法* 进入源码其实就是一个常见的反射调用 method.invoke(target, args)* target 对应 Executor对象* method 对应 Executor的update方法* args 对应 Executor的update方法的参数*/return invocation.proceed();}/*** 这个方法也很好理解* 作用就只有一个:那就是Mybatis在创建拦截器代理时候会判断一次,当前这个类 MyInterceptor 到底需不需要生成一个代理进行拦截,* 如果需要拦截,就生成一个代理对象,这个代理就是一个 {@link Plugin},它实现了jdk的动态代理接口 {@link InvocationHandler},* 如果不需要代理,则直接返回目标对象本身** Mybatis为什么会判断一次是否需要代理呢?* 默认情况下,Mybatis只能拦截四种类型的接口:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler* 通过 {@link Intercepts} 和 {@link Signature} 两个注解共同完成* 试想一下,如果我们开发人员在自定义拦截器上没有指明类型,或者随便写一个拦截点,比如Object,那Mybatis疯了,难道所有对象都去拦截* 所以Mybatis会做一次判断,拦截点看看是不是这四个接口里面的方法,不是则不拦截,直接返回目标对象,如果是则需要生成一个代理** 该方法在 mybatis 加载核心配置文件时被调用*/@Overridepublic Object plugin(Object target) {/** 看了这个方法注释,就应该理解,这里的逻辑只有一个,就是让mybatis判断,要不要进行拦截,然后做出决定是否生成一个代理** 下面代码什么鬼,就这一句就搞定了?* Mybatis判断依据是利用反射,获取这个拦截器 MyInterceptor 的注解 Intercepts和Signature,然后解析里面的值,* 1 先是判断要拦截的对象是四个类型中 Executor、StatementHandler、ParameterHandler、 ResultSetHandler 的哪一个* 2 然后根据方法名称和参数(因为有重载)判断对哪一个方法进行拦截 Note:mybatis可以拦截这四个接口里面的任一一个方法* 3 做出决定,是返回一个对象呢还是返回目标对象本身(目标对象本身就是四个接口的实现类,我们拦截的就是这四个类型)** 好了,理解逻辑我们写代码吧~~~ What !!! 要使用反射,然后解析注解,然后根据参数类型,最后还要生成一个代理对象* 我一个小白我怎么会这么高大上的代码嘛,怎么办?** 那就是使用下面这句代码吧 哈哈* mybatis 早就考虑了这里的复杂度,所以提供这个静态方法来实现上面的逻辑*/return Plugin.wrap(target, this);}/*** 这个方法最好理解,如果我们拦截器需要用到一些变量参数,而且这个参数是支持可配置的,* 类似Spring中的@Value("${}")从application.properties文件获取* 这个时候我们就可以使用这个方法** 如何使用?* 只需要在 mybatis 配置文件中加入类似如下配置,然后 {@link Interceptor#setProperties(Properties)} 就可以获取参数* <plugin interceptor="liu.york.mybatis.study.plugin.MyInterceptor">* <property name="username" value="LiuYork"/>* <property name="password" value="123456"/>* </plugin>* 方法中获取参数:properties.getProperty("username");** 问题:为什么要存在这个方法呢,比如直接使用 @Value("${}") 获取不就得了?* 原因是 mybatis 框架本身就是一个可以独立使用的框架,没有像 Spring 这种做了很多依赖注入的功能** 该方法在 mybatis 加载核心配置文件时被调用 */@Overridepublic void setProperties(Properties properties) {String username = properties.getProperty("username");String password = properties.getProperty("password");// TODO: 2019/2/28 业务逻辑处理...}
}
三个核心方法都加了详细的注释,而且结合案例需求说明问题
那么多文字不想行看,没关系有概括
总结:
1.在mybatis中可被拦截的类型有四种(按照拦截顺序):
Executor:拦截执行器的方法。
ParameterHandler:拦截参数的处理。
ResultHandler:拦截结果集的处理。
StatementHandler:拦截Sql语法构建的处理。
2.各个参数的含义:
@Intercepts:标识该类是一个拦截器;
@Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法;2.1 type:对应四种类型中的一种;2.2 method:对应接口中的哪个方法;2.3 args:对应哪一个方法参数类型(因为可能存在重载方法);
接下来我们看看 Plugin 类
package org.apache.ibatis.plugin;/*** Plugin 类其实就是一个代理类,因为它实现了jdk动态代理接口 InvocationHandler* 我们核心只需要关注两个方法* wrap:* 如果看懂了代码案例1的例子,那么这个方法很理解,这个方法就是 mybatis 提供给开发人员使用的一个工具类方法,* 目的就是帮助开发人员省略掉 反射解析注解 Intercepts 和 Signature,有兴趣的可以去看看源码 Plugin#getSignatureMap 方法** invoke:* 这个方法就是根据 wrap 方法的解析结果,判断当前拦截器是否需要进行拦截,* 如果需要拦截:将 目标对象+目标方法+目标参数 封装成一个 Invocation 对象,给我们自定义的拦截器 MyInterceptor 的 intercept 方法* 这个时候就刚好对应上了上面案例1中对 intercept 方法的解释了,它就是我们要处理自己逻辑的方法,* 处理好了之后是否需要调用目标对象的方法,比如上面说的 打印了sql语句,是否还要查询数据库呢?答案是肯定的* 如果不需要拦截:则直接调用目标对象的方法* 比如直接调用 Executor 的 update 方法进行更新数据库**/
class Plugin implements InvocationHandler {public static Object wrap(Object target, Interceptor interceptor) {// 省略}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 省略}
}
贴一段网上的通用解释吧:
Plugin的wrap方法,它根据当前的Interceptor上面的注解定义哪些接口需要拦截,然后判断当前目标对象是否有实现对应需要拦截的接口,如果没有则返回目标对象本身,如果有则返回一个代理对象。而这个代理对象的InvocationHandler正是一个Plugin。所以当目标对象在执行接口方法时,如果是通过代理对象执行的,则会调用对应InvocationHandler的invoke方法,也就是Plugin的invoke方法。所以接着我们来看一下该invoke方法的内容。这里invoke方法的逻辑是:如果当前执行的方法是定义好的需要拦截的方法,则把目标对象、要执行的方法以及方法参数封装成一个Invocation对象,再把封装好的Invocation作为参数传递给当前拦截器的intercept方法。如果不需要拦截,则直接调用当前的方法。Invocation中定义了定义了一个proceed方法,其逻辑就是调用当前方法,所以如果在intercept中需要继续调用当前方法的话可以调用invocation的procced方法。
这就是Mybatis中实现Interceptor拦截的一个思想
2、在配置文件中添加拦截器
在springboot中要给mybatis加上这个拦截器,有三种方法,前两种方法在启动项目时不会自动调用自定义拦截器的setProperties方法。
- 拦截器顺序
- 1、不同拦截器顺序
- Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
- 2、对于同一个类型的拦截器的不同对象拦截顺序:
- 在 mybatis 核心配置文件根据配置的位置,拦截顺序是 从上往下
- 1、不同拦截器顺序
(1)第一种
直接给自定义拦截器添加一个 @Component注解,当调用sql时结果如下,可以看到拦截器生效了,但是启动时候并没有自动调用setProperties方法。
@Component
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class MybatisInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {//业务代码}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// TODO Auto-generated method stub}}
(2)第二种
在配置类里添加拦截器,这种方法结果同上,也不会自动调用setProperties方法。
@Configuration
public class MybatisConfig {@Beanpublic ConfigurationCustomizer mybatisConfigurationCustomizer() {return new ConfigurationCustomizer() {@Overridepublic void customize(Configuration configuration) {configuration.addInterceptor(new MybatisInterceptor());}};}
}
(3)第三种
这种方法就是跟以前的配置方法类似,在yml配置文件中指定mybatis的xml配置文件,
注意:config-location属性和configuration属性不能同时指定
mybatis:config-location: classpath:mybatis.xmltype-aliases-package: me.zingon.pagehelper.modelmapper-locations: classpath:mapper/*.xml
mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="me.zingon.pacargle.model"/></typeAliases><plugins><plugin interceptor="me.zingon.pagehelper.interceptor.MyPageInterceptor"> <property name="dialect" value="oracle"/></plugin></plugins>
</configuration>
可以看到,在启动项目的时候setProperties被自动调用了
总结:前两种方法可以在初始化自定义拦截器的时候通过 @Value 注解直接初始化需要的参数。
相关文章:

在springboot中给mybatis加拦截器
拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加上我们自己逻辑 Mybatis拦截器设计的一个初衷是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。 mybatis 自定义拦截器 1、实现Interceptor 接口,并添加拦截注解 I…...

[oeasy]python0139_尝试捕获异常_ try_except_traceback
尝试捕获异常 回忆上次内容 变量相加 整型数字变量可以相加字符串变量也可以拼接 但是 字符串 和 整型数字整型数字 和 字符串不能相加 怎么办? 转格式int(“1”)str(2) 可是 如果输入的苹果数量是 字符串"abc" int(“abc”)会发生什么?&…...

树的刷题,嗝
今天忘记带本子了,就没有学习java了,于是一心刷题,好烦遇到了两个奇怪的题目,我没跟题解写的,但是我是没想到奇怪的样例. no.1 617. 合并二叉树 难度简单1221收藏分享切换为英文接收动态反馈 给你两棵二叉树: root1 和 root2 。 想象一下,当你将其中…...

举个栗子~Tableau 技巧(253):让筛选器只显示全部以及需要的类别
用户反馈了一个需求:我的业务数据有很多类别,但其实经常查看的只有几个,Tableau 筛选器能不能设置一下,只显示全部和经常查看的那几个类别? 这个是可以实现的!如下示例,数据类别有:…...

服务器温度过高告警
今天巡检一套rac环境,发现节点二上有Error字样,逐行看完细节是服务器温度过高导致的,半夜又没啥业务的,查看硬件也没任何告警,哎,某品牌的品控确实越来越烂,log一下 Mar 21 02:53:21 hydb1 ker…...

反垃圾邮件产品测试评价方法示意图
声明 本文是学习信息安全技术 反垃圾邮件产品技术要求和测试评价方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 反垃圾邮件产品测试评价方法 测试环境 反垃圾邮件产品的典型测试环境如图1所示。 图1 反垃圾邮件产品典型测试环境示意图 测试设…...

基于vfw的局域网语音聊天室系统源码论文
语音视频聊天 UDP套接字的运用 在实现语音视频聊天时,采用的是基于UDP套接字的点对点模式,而UDP面向的是无连接的数据服务,其套接字的使用如图10所示。 图10 UDP套接字的使用 视频的捕获 利用VFW接口,视频捕获可以分为以下几个步骤: 建立视频采集窗口…...

GoogleTest : 测试框架(单元测试)
GoogleTest googletest: GoogleTest - Google Testing and Mocking Framework googletest 是一个由 Google 的测试技术团队开发的测试框架,它考虑到了谷歌的特定需求和限制。无论你使用的是 Linux、Windows 还是 Mac,只要你编写 C 代码,goo…...

商业银行财富管理“智能原生”能力呈阶梯化,AI助力商业模式趋向多元化发展
易观:金融业的财富管理从经营角度来看,是“客户与渠道管理场景运营产品研发”三位一体共同构建以客户为中心,数据驱动的业务经营体系。其中,“客户与渠道管理”是将客户利益作为核心目标,通过升级用户体验、客户全生命…...

2022年中国云市场份额:阿里云腾讯云下降
我是卢松松,点点上面的头像,欢迎关注我哦! 4月23日,IDC发布《中国公有云服务市场(2022下半年)跟踪》,占据前四的分别为阿里云(40.6%)、华为云(11.0%)、腾讯云(11.0%)、中国电信(8.7%)。咱们说重点,如下图所…...

Redis入门学习笔记【二】Redis缓存
目录 一、Redis缓存 二、Redis使用缓存遇到的问题 2.1 数据一致性 2.2缓存雪崩 2.3 缓存穿透 2.4 缓存击穿 一、Redis缓存 数据缓存是Redis最重要的一个场景,为缓存而生,在springboot中,一般有两种使用方式: 直接通过RedisT…...

go1.20环境安装以及beego框架配置
打开网址下载安装包选择对应安装包来下载安装(个人是windows,下载的1.20.3版本) 默认情况下会安装在C盘,但是我安装在了D盘目录 根据安装提示一步步next,直至完成 go get 在1.18版本之后就弃掉了,换成了install 配置自己的work…...

vue---组件逻辑复用方法:Mixin/HOC/Renderless组件
目录 1、Mixin 2、HOC 3、Renderless组件 下文通过表单校验来分别讲解Mixin/HOC/Renderless组件这三种方式。 1、Mixin 通过mixin将一个公用的validate函数同步到每一个组件中去 mixin使用详细介绍见:vue---mixin混入_maidu_xbd的博客-CSDN博客一个混入对象可…...

阳光万里,祝你上岸——免统考在职研究生
什么是在职研究生 在职研究生,是国家计划内,以在职人员身份,部分时间在职工作,部分时间在校学习的研究生教育的一种类型。在职攻读硕士方式有三种: 1.双证非全日制研究生:为普通高等教育研究生学历&#x…...

大孔树脂型号,A-722,ADS500,ADS600,ADS750,ADS800
一、产品介绍 基于吸附功能的聚苯乙烯特种树脂 Tulsimer ADS-600 是一款没有离子官能基的,由交联聚苯乙烯合成的功能强大的吸附型树脂。 Tulsimer ADS-600 主要应用于水溶液中吸附酚及其化合物,氯代烃等含氯物质,表面活性剂࿰…...

MATLAB在逐渐被Python淘汰吗?
Python和MATLAB都是常用的科学计算工具,但是它们有很多不同之处。 Python是一种通用编程语言,而MATLAB主要是用来做数值计算的。Python的基本数据类型和一般的编程语言一样普遍,但是离开了Numpy这个包,就不再有数组或者矩阵的数据…...

黑盒测试过程中【测试方法】讲解1-等价类,边界值,判定表
在黑盒测试过程中,有9种常用的方法:1.等价类划分 2.边界值分析 3.判定表法 4.正交实验法 5.流程图分析 6.因果图法 7.输入域覆盖法 8.输出域覆盖法 9.猜错法 我们一般用第1种和第2种方法最多。此处简单介绍一下这两种方法,详细介绍其…...

函数栈帧的创建和销毁
文章目录 main函数栈帧的创建为什么局部变量的值是随机值函数是怎么传参的?传参的顺序是什么?函数调用结束是怎么返回的 main函数栈帧的创建 先在栈帧里面为main函数开辟出一块空间,且这块空间由两个寄存器esp和ebp来进行维护,当在调用另一个函数时这两个寄存器就去维护为这…...

测试Ocr工具IronOCR(续:编写图片圈选程序)
上一篇文章学习了IronOCR的基本用法之后,计划做一个加载本地图片后,从图片中圈选某一位置的文字,然后调用IronOCR识别圈选区域文本的程序。本文实现从本地加载图片并完成圈选的功能。 主要的功能包括以下几点: 1ÿ…...

React之Redux的使用
文章目录 Redux 介绍概述Redux 是什么?为什么要使用 Redux?我什么时候应该使用 Redux?Redux 库和工具React-ReduxRedux ToolkitRedux DevTools 扩展 Redux 术语和概念State 管理不可变性 Immutability术语ActionAction CreatorReducerReducer…...

数据库系统概论--第五章课后习题
1.什么是数据库的完整性? 答:数据库的完整性是指数据的正确性和相容性。 2. 数据库的完整性概念与数据库的安全性概念有什么区别和联系? 答: 数据的完整性和安全性是两个不同的概念,但是有一定的联系。前者是为了防止数据库中存…...

小程序开发费用估算:如何控制项目成本?
在当今数字化的时代,小程序已经成为了很多企业和个人开展业务的重要手段。小程序的开发需要耗费时间和资源,因此在项目初期,了解预计的开发费用是非常重要的。本文将详细介绍如何估算小程序开发费用以及如何控制项目成本。 小程序开发费用 …...

【22】linux进阶——文本处理工具:cut、awk、sed
大家好,这里是天亮之前ict,本人网络工程大三在读小学生,拥有锐捷的ie和红帽的ce认证。每天更新一个linux进阶的小知识,希望能提高自己的技术的同时,也可以帮助到大家 另外其它专栏请关注: 锐捷数通实验&…...

Web3D包装生产线 HTML5+Threejs(webgl)开发
生产线三维可视化解决方案就是通过物联网、虚实联动和三维建模等先进技术,以一个3D立体模型展现出来,可以让我们很直观的看到生产线的运作以及对数据的监控。3D运用数据孪生技术可以让工业3D物联网管理系统的界面变得非常的简单易看,并且能够…...

Docker数据管理
目录 一、数据管理方式 1、数据卷 在宿主机上写入数据,传到容器 2、数据卷容器 二、容器互联 一、数据管理方式 用户在使用Docker的过程中,往往需要能查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多…...

在PowerBuilder中更改窗口继承
在PowerBuilder中更改窗口继承 PowerBuilder是Sybase公司的独资子公司PowerSoft推出的应用于客户机/服务器体系结构下的应用程序开发工具,也是一种面向对象的图形化交互式开发工具。PowerBuilder 以其简洁高效的集成开发环境、 强大的数据窗口技术、几乎无所不能的数据库访问…...

CS:APP 第7章链接分步编译(cpp/cc1/as/ld)遇到的问题
环境 WSL Ubuntu 22.04.2 LTS gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0 问题 问题一 cc1 命令找不到 cc1 命令在 /usr/lib/gcc/x86_64-linux-gnu/11/cc1 里,注意不同操作系统等可能 cc1 的位置不一样,可以使用 find 或者 locate 命令搜索。 通过下…...

Jsp+sql智能道路交通信息管理系统的设计与实现(论文+系统+开题报告+答辩PPT+外文翻译)
伴随着社会经济的发展,交通在人们经济和社会活动中扮演着越来越重要的角色。因而,交通管理的水平和质量就和我们的日产生活紧密相连,并且直接影响着投资的环境和城市的面貌。因此,每个城市都做了很多关于改善交通和发展智能交通系…...

自动化工具 WEB 自动化工具
背景 使用自动化测试框架编写用例的时候,维护元素信息以及脚本较为麻烦。对应新手来说,编写脚本的能力有限,使用工具会更容易入手。最重要的是可视化操作让我觉得体验舒服。 演示地址 地址:hippo 账号:test 密码&am…...

AutoGPT 安装指南,使用避坑要点
最近, AIGC 中最火的可能就当属于 AutoGPT 了吧,首先简单介绍一下AutoGPT 背景 AutoGPT 是基于 ChatGPT API 接口开发,项目首推 GPT-4 模型,但 OpenAI 账号 API 只有 gpt-3.5-turo 权限同样也可以使用。 项目在 github 上获取的…...