在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…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...