mybatis interceptor原理_mybatis拦截器获取表名

mybatis interceptor原理_mybatis拦截器获取表名看了很多博客文章和,mybatis的拦截器概念还是不能很好理解,可能是因为自己基础不好或者理解方式和他人不同吧,所以决定自己花时间好好捋捋,然后把理解后的总结记录下来,供他人参考,也许你们的理解和我也不同,但是不妨花几分钟时间看看,说不定能帮助你文章主要是讲解org.apache.ibatis.plugin包下的Interceptor类和org.apache.ibatis….

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

看了很多博客文章和,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 * * 如果还是不能理解,我再举一个需求案例:看下面方法代码里面的需求 * * 该方法在运行时调用 */
    @Override
    public 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 加载核心配置文件时被调用 */
    @Override
    public 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 加载核心配置文件时被调用 */
    @Override
    public 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) { 
   
        // 省略
    }

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/195660.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • C++Qt入门(1)—Qt简介,第一个Qt程序,Qt按钮

    C++Qt入门(1)—Qt简介,第一个Qt程序,Qt按钮文章目录一、QT简介1.什么是QT?2.Qt的发展史?二、第一个Qt程序1.路径名,文件名中不能有中文2.创建默认窗口类3.main函数4.对.pro文件的解释5.QtCreator快捷键6.QPushButton的创建7.对象树(了解)8.QT中的坐标系一、QT简介1.什么是QT?Qt是一个跨平台的C++图形用户界面应用程序框架2.Qt的发展史?1991年Qt最早由奇趣科技开发1996年进入商业领域,是目前流行的Linux桌面环境KDE的基础……(略)3.Qt支持的平台4.Qt的下载与

  • C++STL容器总结[通俗易懂]

    持续更新中!!!各大容器的特点:1.可以用下标访问的容器有(既可以插入也可以赋值):vector、deque、map;特别要注意一下,vector和deque如果没有预先指定大小,是不能用下标法插入元素的…

  • TeX Live2018_latex安装教程

    TeX Live2018_latex安装教程Y·S2018年8月5日15:00:32点击链接https://tug.org/texlive/注:Latex不止TeX这一种,这里只给出了TeX的安装,如果想尝试别的软件的同学可以自行寻找其他教程。并执行如下操作:第一步第二步第三步第四步第五步装载下载好了的TexLive安装包:分以下几种情况:…

  • java中session的用法与原理

    java中session的用法与原理https://www.cnblogs.com/xdp-gacl/p/3855702.htmlsession简介在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的s…

  • 5.25 使用半调图案命令制作影印效果 [Illustrator CC教程][通俗易懂]

    5.25 使用半调图案命令制作影印效果 [Illustrator CC教程][通俗易懂]原文:http://coolketang.com/staticDesign/5a97b8c4128fe1189bd33ddc.html1.本节课将为您演示[半调图案]命令的使用。首先选择文档中,待编辑的图像。 2.接着依次点击[效果&gt;效果画廊]命令。 3. 4.在弹出的效果画廊窗口中,点击[缩小]按钮,在预览区缩小显示当前的图像。 5.点击[素描]左侧的三角形,显示[素描]效果组…

  • 【网盘搭建】使用Rclone挂载Google Drive扩容服务器存储,实现网盘无限容量[通俗易懂]

    【网盘搭建】使用Rclone挂载Google Drive扩容服务器存储,实现网盘无限容量[通俗易懂]一,前言1,Rclone是什么Rclone是一个开源的命令行程序,用于管理云存储上的文件。它是云供应商Web存储界面的功能丰富的替代方案。超过50种云存储产品支持Rclone,包括S3对象存储,GoogleDrive,OneDrive等业务和消费者文件存储服务以及标准传输协议。2,它能用来干嘛可以备份(和加密)文件到云存储。从云存储还原(和解密)文件。将云数据镜像到其他云服务或本地。将数据迁移到云,或在云存储供应商之间迁移。将多个加密的,缓存的或多样化的云存储作为磁盘挂载。3,项目地址Gith

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号