Mybatis拦截器的使用及其源码详解

article/2025/10/1 8:17:39

Mybatis拦截器的使用及其源码详解

  • Mybatis相关全览
  • 一、简介
    • 执行与添加顺序
    • 拦截器生效入口
  • 二、使用
    • 例子
  • 三、原理
    • 加载入口
    • 生成代理
      • 遍历拦截器
      • 匹配&生成代理
  • 四、实践例子

本文用的是3.5.10版本
源码地址:https://github.com/mybatis/mybatis-3/releases
文档地址:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

Mybatis相关全览

一. Mybatis源码详解
二. Mybatis二级缓存详解
三. Mybatis三大执行器介绍
四. Mybatis拦截器源码详解

个人博客: 全是干货,相信不会让你失望

一、简介

拦截器我相信大家已经非常熟悉了,Mybatis也有拦截器?Mybatis提供了一种插件(plugin)的功能,实际上就是拦截器,像我们平时使用的分页插件也是基于此来实现的

在Mybatis中拦截器可以针对4个点拦截处理,也可以说是4个接口:

  • Executor:拦截执行器的相关方法,可以对这些方法增强处理
  • ParameterHandler:拦截参数的处理,可以对传参做统一处理
  • ResultSetHandler:拦截结果集的处理,可以对返回的结果做统一处理
  • StatementHandler:拦截Sql构建的处理

执行与添加顺序

拦截器主要是拦截以上4个接口,对里面的方法进行增强处理,所以如果以方法来看拦截器的执行顺序是与这几个接口里面的方法在Mybatis执行流程里面的顺序有关,但如果以接口来看拦截器的执行顺序,大概流程是这样(不了解的可以去看看 Mybatis流程源码):

ExecutorParameterHandlerStatementHandlerResultSetHandler

拦截器生效入口

拦截器是什么时候介入到执行流程中来的呢?

以上述4个接口为例,所以有4个介入入口,均是在执行流程中初始化的时候介入的,如下图:

Configuration内部:

在这里插入图片描述

二、使用

使用拦截器其实很简单,我们只需要打上两个注解,实现一个接口,并在配置文件中配置一下就可以了

要打上的两个注解:

  • @Intercepts: 该注解等于是个标识,标识该类是个拦截器,需要配合@Signature来使用
  • @Signature: 该注解也是个标识,表示要拦截的接口以及接口内的哪个方法,有三个参数
    • type :代表我们要拦截的是哪个接口(4个里面选一个)
    • method :代表我们要拦截的是接口里面的哪一个方法(从接口里面去选一个)
    • args :要拦截的方法里面的参数类型,方法里面有几个传参这里就要写几个(因为方法存在重载,名称还无法确定唯一性)

要实现的接口:

Interceptor,内部有三个方法,一个必须要实现,两个随意

  • intercept():必须要实现的方法,这里就可以处理我们的逻辑
  • plugin():可选择实现,返回目标对象或者代理对象
  • setProperties():获取参数,可以从外部获取一些配置参数

下面准备了一个小例子,大家先感受一下

例子

/*** 这里我们拦截Executor里面的query和update方法* 一个@Signature 代表要拦截的一个方法*/
@Intercepts({/*** type     :代表我们要拦截的是哪个接口(4个里面选一个)* method   :代表我们要拦截的是接口里面的哪一个方法(从接口里面去选一个)* args     :要拦截的方法里面的参数类型,方法里面有几个传参这里就要写几个(因为方法存在重载,名称还无法确定唯一性)**/@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})
})
public class DemoInterceptor1 implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {//被代理对象Object target = invocation.getTarget();//代理方法Method method = invocation.getMethod();//方法参数Object[] args = invocation.getArgs();// do something ...方法拦截前执行代码块System.out.println("方法拦截前执行 do something ...");// 本方法执行(这就是执行被拦截的源方法)Object result = invocation.proceed();// do something ...方法拦截后执行代码块System.out.println("方法拦截后执行 do something ...");return result;}/*** 通过该方法决定要返回的对象是目标对象还是对应的代理* 一般就两种情况(乱来小心报错):* 1. return target;  直接返回目标对象,相当于当前Interceptor没起作用,不会调用上面的intercept()方法* 2. return Plugin.wrap(target, this);  返回代理对象,会调用上面的intercept()方法*/@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}/*** 用于获取在Configuration初始化当前的Interceptor时候设置的一些参数** @param properties Properties参数*/@Overridepublic void setProperties(Properties properties) {}
}

配置文件中配置:

  <plugins><plugin interceptor="org.apache.ibatis.interceptor.DemoInterceptor"/></plugins>

由于现在一般都结合springboot开发,这种配置文件的方式可能都被舍弃了,所以在springboot中我们只需要采用@Configuration+@Bean的方式把我们拦截器注入就可以了

三、原理

拦截器类似AOP都属于切面编程思想,底层原理都是动态代理

加载入口

我们以XML配置为例,在解析XML配置的时候就会解析插件(plugins)节点:

在这里插入图片描述

内部就会加载这些拦截器到configuration当中:

在这里插入图片描述

生成代理

还记得上面拦截器的生效入口吗?都执行了一次pluginAll方法吧

  1. 该方法内部就会遍历所有拦截器,执行拦截器里面的plugin方法
  2. 然后通过拦截器打上的那两个注解去匹配
  3. 有匹配的我就给你生成一个代理对象返回,没有就返回原本的对象

在这里插入图片描述

遍历拦截器

在这里插入图片描述

该方法就是我们实现接口后可选择实现的方法之一,有个默认实现的逻辑

在这里插入图片描述

匹配&生成代理

Plugin.wrap方法 就会就获取拦截器上的注解,然后与拦截器的目标对象去匹配,如果匹配上了说明在拦截的范围所以会生成一个代理对象,Plugin同时又实现了InvocationHandler接口,说明代理对象最后执行的时候,会执行Plugin.invoke方法,内部就会判断执行的方法是否是我需要拦截的方法,是则会执行拦截器的拦截方法,不是则执行原方法

在这里插入图片描述

原理咧就是这样,动态代理,对动态代理不了解的,可以先去了解一下

四、实践例子

这里根据上面demo搞了个例子,用处就是打印执行日志,执行的具体方法、执行的完整的SQL语句、执行时间

/*** 这里我们拦截Executor里面的query和update方法* 一个@Signature 代表要拦截的一个方法*/
@Intercepts({/*** type     :代表我们要拦截的是哪个接口(4个里面选一个)* method   :代表我们要拦截的是接口里面的哪一个方法(从接口里面去选一个)* args     :要拦截的方法里面的参数类型,方法里面有几个传参这里就要写几个(因为方法存在重载,名称还无法确定唯一性)**/@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})
})
public class DemoInterceptor1 implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = null;if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[1];}String sqlId = mappedStatement.getId();BoundSql boundSql = mappedStatement.getBoundSql(parameter);Configuration configuration = mappedStatement.getConfiguration();long sqlStartTime = System.currentTimeMillis();Object re = invocation.proceed();long sqlEndTime = System.currentTimeMillis();// 获取SQL执行语句String s = sqlHandler(boundSql, configuration);// 打印mysql执行 日志  这里为了方便,不要学我System.out.println("-----------------------------------------------------------------");System.out.println("SQL的Mapper方法: "+sqlId);System.out.println("SQL: "+s);System.out.println("SQL执行时间:" + (sqlEndTime - sqlStartTime) + " ms");System.out.println("-----------------------------------------------------------------");return re;}/*** @Author czl* @Description   sql语句里面的?替换成真实的参数**/private String sqlHandler(BoundSql boundSql,Configuration configuration){// 获取mapper里面方法上的参数Object sqlParameter = boundSql.getParameterObject();TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();// sql原始语句(?还没有替换成我们具体的参数)String sql = boundSql.getSql().replaceAll("[\\s]+", " ");List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} else if (sqlParameter == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(sqlParameter.getClass())) {value = sqlParameter;} else {MetaObject metaObject = configuration.newMetaObject(sqlParameter);value = metaObject.getValue(propertyName);}// 上面都是搬的源码里面的  这里的value应该还需要处理,我这里就无脑转String了sql = sql.replaceFirst("\\?", value.toString());}}}return sql;}/*** 通过该方法决定要返回的对象是目标对象还是对应的代理* 一般就两种情况(乱来小心报错):* 1. return target;  直接返回目标对象,相当于当前Interceptor没起作用,不会调用上面的intercept()方法* 2. return Plugin.wrap(target, this);  返回代理对象,会调用上面的intercept()方法*/@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}/*** 用于获取在Configuration初始化当前的Interceptor时候设置的一些参数** @param properties Properties参数*/@Overridepublic void setProperties(Properties properties) {}

结果如下:

在这里插入图片描述


http://chatgpt.dhexx.cn/article/YoBKEDqT.shtml

相关文章

Mybatis拦截器Interceptor

前言 最近项目使用Mybatis拦截器对数据进行加解密&#xff0c;以下记录如何将拦截器集成到项目中以及在使用过程中踩过的一些小坑&#xff0c;与君共勉 1.Myabtis拦截器是什么&#xff1f; MyBatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用&#xff0c…

Springboot 自定义mybatis 拦截器,实现我们要的扩展

前言 相信大家对拦截器并不陌生&#xff0c;对mybatis也不陌生。 有用过pagehelper的&#xff0c;那么对mybatis拦截器也不陌生了&#xff0c;按照使用的规则触发sql拦截&#xff0c;帮我们自动添加分页参数 。 那么今天&#xff0c;我们的实践 自定义mybatis拦截器也是如此&a…

mybatis 拦截器

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

Mybatis——拦截器Interceptor

本篇介绍Mybatis拦截器/插件相关知识&#xff0c;包括相关的类及作用、拦截器有哪几种、拦截器可以在Mybatis的执行过程中的哪些节点起作用、拦截器如何使用、相关的API&#xff0c;最后附上代码示例。 1. Mybatis拦截器是什么&#xff1f;一般用途&#xff1f; MyBatis允许使…

vue后台管理框架(iview + vue)

iview官网&#xff1a;https://www.iviewui.com/docs/introduce vueiview后台管理文档&#xff1a;https://lison16.github.io/iview-admin-doc/#/ vueiview后台管理线上示例&#xff1a;https://admin.iviewui.com/login vueiview后台管理github&#xff1a;https://github.co…

基于ThinkPHP6+Layui的后台管理框架

项目介绍 一款 PHP 语言基于 ThinkPhp6.x、Layui、MySQL等框架精心打造的一款模块化、插件化、高性能的前后端分离架构敏捷开发框架&#xff0c;可用于快速搭建前后端分离后台管理系统&#xff0c;本着简化开发、提升开发效率的初衷&#xff0c;框架自研了一套个性化的组件&am…

Quickadmin:基于ThinkPhp6+Vue+ElementUI后台管理框架

Quickadmin&#xff1a;基于ThinkPhp6VueElementUI后台管理框架 简介 QuickAdmin 是一款基于ThinkPHP 6.x&#xff0c;ElementUI&&Vue 2.x前后端分离后台管理框架&#xff0c;通过框架自带的在线代码生成器&#xff0c;可以轻松的实现后台管理的curd&#xff08;增删…

后台管理怎样用html实现,后台管理实现

京淘后台管理实现 1.1 商品列表展现 1.1.1 商品POJO对象 1.1.2 表格数据页面结构 1.1.3 请求URL地址 说明&#xff1a;如果采用UI框架并且添加了分页插件&#xff0c;则会自动的形成如下的URL请求地址 1.1.4 编辑ItemController 1.1.5 编辑ItemService 1.1.6 编辑ItemMapper 手…

【React项目架构 】+后台管理系统cms实操

React项目架构 一、 react脚手架 (一) 、yarn 安装 (二) 、react安装 npx create-react-app [-g] npm i react17.0.0 react-dom17.0.0 babel-standalone6.26.0 npm i react-router-dom npm i redux4.1.1 --save npm i redux-thunk2.4.1 npm i redux-persist npm install an…

Python实现的一个简洁轻快的后台管理框架.支持拥有多用户组的RBAC管理后台,不用配置各种运行环境

Mini Admin 完整代码下载地址&#xff1a;Python实现的一个简洁轻快的后台管理框架.支持拥有多用户组的RBAC管理后台 Mini Admin,一个简洁轻快的后台管理框架.支持拥有多用户组的RBAC管理后台 &#x1f680; 应用场景&#xff1a;2-5人的管理团队&#xff0c;需要管理的资源数…

vue——后台管理系统框架

效果图 代码: <template><el-container class="box-el-container"><el-aside class="box-el-aside" :style="{width:isCollapse?60px:200px}"><el-col :span="12" class="el-menu-aside"><!…

基于ThinkPHP6+Layui通用后台管理框架

项目介绍 一款 PHP 语言基于 ThinkPhp6.x、Layui、MySQL等框架精心打造的一款模块化、插件化、高性能的前后端分离架构敏捷开发框架&#xff0c;可用于快速搭建前后端分离后台管理系统&#xff0c;本着简化开发、提升开发效率的初衷&#xff0c;框架自研了一套个性化的组件&am…

Rust学习指南(一)安装RUST后台管理框架

Windows安装 Rust安装非常简单&#xff0c;只要将Visual Studio的 Visual Studio的构建工具或者Visual Stuido 2022的构件工具安装即可。当被问及要安装哪些内容时&#xff0c;请确保已选择 “C build tools”&#xff0c;并包括 Windows 10 SDK 和英文语言包。具体可以参考这个…

php是一种通用开源,caozha-admin(PHP网站后台管理框架)

caozha-admin 后台管理框架 1.7.2 caozha-admin是一个通用的PHP网站后台管理框架&#xff0c;基于开源的ThinkPHP开发&#xff0c;特点&#xff1a;易上手&#xff0c;零门槛&#xff0c;界面清爽极简&#xff0c;极便于二次开发。 基础功能 1、系统设置 2、管理员管理 3、权限…

移动端后台管理系统框架

创建此项目的初衷 目前移动端越来越重要&#xff0c;好多项目都从PC端转移到了移动端。 前一段给客户做了一个PC和M自适应的项目&#xff0c;用vue-element-admin框架&#xff0c;手机端也能用&#xff0c;但体验有点差&#xff0c;客户改了好多。本来是好意&#xff0c;客户只…

(若依)RuoYi后台管理框架前端

若依后台管理 官方网址 后台管理二开推荐 官网的源码地址可供下载前后端代码 最近进到公司后跟着团队接了两家公司的App一套的开发&#xff0c;到公司的第一天就是先配置开发环境 安装软件之类的 第二天 带着熟悉RuoYi框架 很巧带我的大哥和我是一个学校毕业的 是大师兄 若依…

后台管理系统框架bootstrap中文版

简介&#xff1a; 后台管理系统框架bootstrap中文版 网盘下载地址&#xff1a; http://kekewl.cc/8TLdhidu7gi0 图片&#xff1a;

SpringBoot后台管理系统框架

SpringBoot后台管理系统框架 SpringBoot后台管理系统功能介绍 登录 注册 用户列表和添加功能 只是个框架 实现了shiro权限控制, 详细的shiro使用 一个模板项目系统 只有少量功能 使用技术 SpringBoot框架 Mysql数据库 redis shiro权限 thymeleaf(前端) 功能展示 shi…

thinkphp 后台管理框架swiftadmin的使用

SwiftAdmin 极速开发框架 这款框架是基于ThinkPHP Layui 完美契合&#xff0c;在开发上采用最精简最高效的做法去完成业务系统的需求&#xff0c;是一款优秀的中后台极速开发解决方案。 可以很卡快的上手开发&#xff0c;而且他的文档也很完善&#xff0c;代码量不是很多&…

layui搭建后台管理框架

简介&#xff1a;layui&#xff08;谐音&#xff1a;类UI) 是一款采用自身模块规范编写的前端 UI 框架&#xff0c;遵循原生 HTML/CSS/JS 的书写与组织形式&#xff0c;门槛极低&#xff0c;拿来即用。其外在极简&#xff0c;却又不失饱满的内在&#xff0c;体积轻盈&#xff0…