看了很多博客文章和,mybatis 的拦截器概念还是不能很好理解,
可能是因为自己基础不好或者理解方式和他人不同吧,所以决定自己花时间好好捋捋,
然后把理解后的总结记录下来,供他人参考,也许你们的理解和我也不同,
但是不妨花几分钟时间看看,说不定能帮助你

文章主要是讲解 org.apache.ibatis.plugin 包下的 Interceptor 类
和 org.apache.ibatis.plugin 包下的 Plugin 类,而且主要是以代码和注释的方式来说明问题。

先看些基本概念(废话):

1 拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加上我们自己逻辑
2 Mybatis拦截器设计的一个初衷是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。

实现一个自定义拦截器

Mybatis为我们提供了一个Interceptor接口,通过实现该接口就可以定义我们自己的拦截器。请耐心看完代码注释
代码案例1

/*** mybatis 自定义拦截器* 三步骤:*  1 实现 {@link Interceptor} 接口*  2 添加拦截注解 {@link Intercepts}*  3 配置文件中添加拦截器** 1 实现 {@link Interceptor} 接口*      具体作用可以看下面代码每个方法的注释* 2 添加拦截注解 {@link Intercepts}*      mybatis 拦截器默认可拦截的类型只有四种,即四种接口类型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler*      对于我们的自定义拦截器必须使用 mybatis 提供的注解来指明我们要拦截的是四类中的哪一个类接口*      具体规则如下:*          a:Intercepts 标识我的类是一个拦截器*          b:Signature 则是指明我们的拦截器需要拦截哪一个接口的哪一个方法*              type    对应四类接口中的某一个,比如是 Executor*              method  对应接口中的哪类方法,比如 Executor 的 update 方法*              args    对应接口中的哪一个方法,比如 Executor 中 query 因为重载原因,方法有多个,args 就是指明参数类型,从而确定是哪一个方法* 3 配置文件中添加拦截器*      拦截器其实就是一个 plugin,在 mybatis 核心配置文件中我们需要配置我们的 plugin :*          <plugin interceptor="liu.york.mybatis.study.plugin.MyInterceptor">*              <property name="username" value="LiuYork"/>*              <property name="password" value="123456"/>*          </plugin>** 拦截器顺序* 1 不同拦截器顺序:*      Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler** 2 对于同一个类型的拦截器的不同对象拦截顺序:*      在 mybatis 核心配置文件根据配置的位置,拦截顺序是 从上往下*/
@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  业务逻辑处理...}
}

三个核心方法都加了详细的注释,而且结合案例需求说明问题
不知道能否帮助你理解,我的表达能力有限,更别说自己的文笔表达了~~~

接下来我们看看 Plugin 类

代码案例2

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拦截的一个思想

真正理解mybatis拦截器以及Interceptor和Plugin作用相关推荐

  1. Mybatis源码分析之(六)mybatis拦截器(Interceptor)的实现原理

    文章目录 前言 InterceptorChain保存所有的Interceptor 创建四大对象都走Configuration InterceptorChain增强对象方法 Plugin封装动态代理,让 ...

  2. MyBatis 插件之拦截器(Interceptor),拦截查询语句

    一.背景 在很多业务场景下我们需要去拦截sql,达到不入侵原有代码业务处理一些东西,比如:分页操作,数据权限过滤操作,SQL执行时间性能监控等等,这里我们就可以用到Mybatis的拦截器Interce ...

  3. 对拦截器(Interceptor)和过滤器(Filter)的理解和总结

    一.概念认知 1. 拦截器(Interceptor): 概念: java里的拦截器是动态拦截Action调用的对象.它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在 ...

  4. 面试官:你能说说MyBatis拦截器原理吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Format cnblogs.com/fangjian042 ...

  5. 犯罪心理解读Mybatis拦截器

    原文链接:"犯罪心理"解读Mybatis拦截器 Mybatis拦截器执行过程解析 文章写过之后,我觉得 "Mybatis 拦截器案件"背后一定还隐藏着某种设计动 ...

  6. MyBatis拦截器原理探究MyBatis拦截器原理探究

    MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis拦截 ...

  7. MyBatis拦截器有哪些以及分析

    MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis 允许你在已映射语句执行过程中 ...

  8. MyBatis拦截器原理探究

    MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis 允 ...

  9. MyBatis拦截器原理探究MyBatis拦截器原理探究 1

    MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis拦截 ...

最新文章

  1. qtablewidget限制输入类型_对敏感型电子信号输入实施过压保护的可靠新方法
  2. 声网sdk android接入,Android 集成声网Agora 音视频实时通话(三)
  3. 【数据挖掘】高斯混合模型 ( 模型简介 | 软聚类 | 概率作用 | 高斯分布 | 概率密度函数 | 高斯混合模型参数 | 概率密度函数 )
  4. c语言题库杭电,C语言杭电18级B卷试题答案.doc
  5. Android开发技巧——大图裁剪
  6. Dubbo的优化 --- 开发时使用
  7. Shiro——RememberMe
  8. 五、顺序访问性能测试
  9. NYOJ399 - 整除个数
  10. iOS开源项目周报1229
  11. MYSQL数据库ANY的用法_数据库——IN、ANY、SOME 和 ALL 操作符的使用
  12. html条件查询,高级查询条件设置- 通用查询-报表设计初级教程
  13. 常见的hash函数算法
  14. python实操实例100例_趣学Python算法100例
  15. indexedDB 存储 js文件 xml文件 大文件缓存 并导出使用
  16. Service Mesh Summit 服务网格峰会 2022 正在报名中
  17. Windows 11答疑:大家最关心的10个问题
  18. 月薪2千到年薪百万,厂妹到高盛程序员,她书写了一个女孩的史诗
  19. 世态炎凉,冷暖自知-一名某易公司员工自述的裁员经历
  20. echarts + echarts-gl - 使用geo3d + map3d + scatter3D做3d地图

热门文章

  1. <Android开发> Android vold - 第七篇 vold 的runCommand()方法解析
  2. 如何把canvas元素作为网站背景总结详解
  3. HT1621B驱动液晶LCD连接及程序
  4. Java基础(一) 八大基本数据类型
  5. sizeof(int)为什么输出的是4而不是2呢,int类型究竟占几个字节
  6. 计算机excel按F4是那个公式,Excel中F4有哪些功能 Excel如何巧妙使用F4
  7. python操作微信自动发消息(微信聊天机器人)
  8. 用python制作网盘_3分钟教你用python制作一个简单词云
  9. php偷单,最简单的PHP小偷源码
  10. 【加密】对称加密DES和非对称加密AES、数字签名|非对称加密--公钥和私钥的区别