面试官:mybatis插件有什么用途?说说底层原理?我竟然不会。。

Java技术栈 Java技术栈     2022-11-30     771

关键词:

来源:https://www.cnblogs.com/chenpi/p/10498921.html

背景

关于Mybatis插件,大部分人都知道,也都使用过,但很多时候,我们仅仅是停留在表面上,知道Mybatis插件可以在DAO层进行拦截,如打印执行的SQL语句日志,做一些权限控制,分页等功能;但对其内部实现机制,涉及的软件设计模式,编程思想往往没有深入的理解。

本篇案例将帮助读者对Mybatis插件的使用场景,实现机制,以及其中涉及的编程思想进行一个小结,希望对以后的编程开发工作有所帮助。

注:本案例以mybatis 3.4.7-SNAPSHOT版本为例。

PS:文章是挺久之前写的,当时花了一些心思,存到电脑的word里,今天正好看到,就是里面的源码都是图片,哈哈哈,凑合着看吧。

Mybatis插件典型适用场景

分页功能

mybatis的分页默认是基于内存分页的(查出所有,再截取),数据量大的情况下效率较低,不过使用mybatis插件可以改变该行为,只需要拦截StatementHandler类的prepare方法,改变要执行的SQL语句为分页语句即可;

公共字段统一赋值

一般业务系统都会有创建者,创建时间,修改者,修改时间四个字段,对于这四个字段的赋值,实际上可以在DAO层统一拦截处理,可以用mybatis插件拦截Executor类的update方法,对相关参数进行统一赋值即可;

性能监控

对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间;

其它

其实mybatis扩展性还是很强的,基于插件机制,基本上可以控制SQL执行的各个阶段,如执行阶段,参数处理阶段,语法构建阶段,结果集处理阶段,具体可以根据项目业务来实现对应业务逻辑。

Mybatis插件介绍

什么是Mybatis插件

与其称为Mybatis插件,不如叫Mybatis拦截器,更加符合其功能定位,实际上它就是一个拦截器,应用代理模式,在方法级别上进行拦截。

支持拦截的方法

  • 执行器Executor(update、query、commit、rollback等方法);
  • 参数处理器ParameterHandler(getParameterObject、setParameters方法);
  • 结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
  • SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法);

拦截阶段

那么这些类上的方法都是在什么阶段被拦截的呢?为理解这个问题,我们先看段简单的代码(摘自mybatis源码中的单元测试SqlSessionTest类),来了解下典型的mybatis执行流程,如下代码所示:

以上代码主要完成以下功能:

  • 读取mybatis的xml配置文件信息
  • 通过SqlSessionFactoryBuilder创建SqlSessionFactory对象
  • 通过SqlSessionFactory获取SqlSession对象
  • 执行SqlSession对象的selectList方法,查询结果
  • 关闭SqlSession

如下是时序图,在整个时序图中,涉及到mybatis插件部分已标红,基本上就是体现在上文中提到的四个类上,对这些类上的方法进行拦截。

Mybatis插件实现机制

插件配置信息的加载

先来看下mybatis是如何加载插件配置的,对应的xml配置信息如下:

对应的解析代码如下,主要做以下工作:

  1. 根据解析到的类信息创建Interceptor对象;
  2. 调用setProperties方法设置属性变量;
  3. 添加到Configuration的interceptorChain拦截器链中;

以上逻辑对应的时序图如下:

代理对象的生成

Mybatis插件的实现机制主要是基于动态代理实现的,其中最为关键的就是代理对象的生成,所以有必要来了解下这些代理对象是如何生成的。

Executor代理对象

ParameterHandler代理对象

ResultSetHandler代理对象

StatementHandler代理对象

观察源码,发现这些可拦截的类对应的对象生成都是通过InterceptorChain的pluginAll方法来创建的,进一步观察pluginAll方法,如下:

遍历所有拦截器,调用拦截器的plugin方法生成代理对象,注意生成代理对象重新赋值给target,所以如果有多个拦截器的话,生成的代理对象会被另一个代理对象代理,从而形成一个代理链条,执行的时候,依次执行所有拦截器的拦截逻辑代码;

接下来看一下我们在编写拦截器的时候,一个典型的plugin方法实现方式,如下:

再进一步查看wrap方法,如下:

典型的动态代理实现,调用的是Proxy.newProxyInstance方法来生成代理对象。

以上逻辑对应的时序图如下,这里我们假设声明了两个拦截器,那么在创建target代理对象的时候,最终返回的代理对象proxy2,实际上代理了proxy1,而proxy1又代理了target,:

拦截逻辑的执行

由于真正去执行Executor、ParameterHandler、ResultSetHandler和StatementHandler类中的方法的对象是代理对象(建议将代理对象转为class文件,反编译查看其结构,帮助理解),所以在执行方法时,首先调用的是Plugin类(实现了InvocationHandler接口)的invoke方法,如下:

首先根据执行方法所属类获取拦截器中声明需要拦截的方法集合;

判断当前方法需不需要执行拦截逻辑,需要的话,执行拦截逻辑方法(即Interceptor接口的intercept方法实现),不需要则直接执行原方法。

可以关注下Interceptor接口的intercept方法实现,一般需要用户自定义实现逻辑,其中有一个重要参数,即Invocation类,通过改参数我们可以获取执行对象,执行方法,以及执行方法上的参数,从而进行各种业务逻辑实现,一般在该方法的最后一句代码都是invocation.proceed()(内部执行method.invoke方法),否则将无法执行下一个拦截器的intercept方法。

以上逻辑对应的时序图如下,这里我们以执行executor对象的query方法为例,且假设有两个拦截器存在:

Mybatis插件开发例子

这里以分页插件为例,来了解下一般mybatis插件的编写规则,如下所示:

主要需要实现三个方法

  1. intercept:在此实现自己的拦截逻辑,可从Invocation参数中拿到执行方法的对象,方法,方法参数,从而实现各种业务逻辑, 如下代码所示,从invocation中获取的statementHandler对象即为被代理对象,基于该对象,我们获取到了执行的原始SQL语句,以及prepare方法上的分页参数,并更改SQL语句为新的分页语句,最后调用invocation.proceed()返回结果。
  2. plugin:生成代理对象;
  3. setProperties:设置一些属性变量;

小结

简单的说,mybatis插件就是对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑,所以真正要用好mybatis插件,主要还是要熟悉这四个接口的方法以及这些方法上的参数的含义;

另外,如果配置了多个拦截器的话,会出现层层代理的情况,即代理对象代理了另外一个代理对象,形成一个代理链条,执行的时候,也是层层执行;

关于mybatis插件涉及到的设计模式和软件思想如下:

  1. 设计模式:代理模式、责任链模式;
  2. 软件思想:AOP编程思想,降低模块间的耦合度,使业务模块更加独立;

一些注意事项:

  1. 不要定义过多的插件,代理嵌套过多,执行方法的时候,比较耗性能;
  2. 拦截器实现类的intercept方法里最后不要忘了执行invocation.proceed()方法,否则多个拦截器情况下,执行链条会断掉;

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

面试官:你天天用lombok,说说它什么原理?我竟然答不上来…

作者:九年义务教育漏网之鱼链接:https://juejin.cn/post/6844904072789622792相信大家在项目中都使用过Lombok,因为能够简化我们许多的代码,但是该有的功能一点也不少。那么lombok到底是个什么呢,lombok是一个可以通过简单的注解的形... 查看详情

面试官:说说什么是java内存模型(jmm)?(代码片段)

...2.Java线程与主内存的关系3.3.线程间通信4.有态度的总结在面试中,面试官经常喜欢问:『说说什么是Java内存模型(JMM)?』面试者内心狂喜 查看详情

面试官:说说mybatis二级缓存?关联刷新实现?我懵b了。。(代码片段)

来源:blog.csdn.net/qq_38245668/article/details/1058032981、MyBatis缓存介绍Mybatis提供对缓存的支持,但是在没有配置的默认情况下,它只开启一级缓存,二级缓存需要手动开启。一级缓存只是相对于同一个SqlSession而言。也就... 查看详情

面试官:说说spring事务设计原理?面试必问!

作者:张丰哲来源:www.jianshu.com/p/1becdc376f5d前言事务,是描述一组操作的抽象,比如对数据库的一组操作,要么全部成功,要么全部失败。事务具有4个特性:Atomicity(原子性),Consistency... 查看详情

面试官:说说netty断开连接的原理

参考技术A多路复用器(Selector)接收到OP_READ事件:处理OP_READ事件:NioSocketChannel.NioSocketChannelUnsafe.read()关闭连接,会触发OP_READ事件:到了最后,关闭selection上的selectionkey,这样selector上就不会再发生该channel上的各种事件了。如果发送... 查看详情

redis综述篇:与面试官彻夜长谈redis缓存持久化淘汰机制哨兵集群底层原理!...

...,和你一起掌握更多数据库知识于哥你好,最近面试挺多的,尤其是在问到java面试题,Redis被问的特别多,比如Redis为什么面试都问我却不懂?因为它快吗?它为什么快?今天分享一篇好的问题࿰... 查看详情

mybatis的缓存讲解

...的一个朋友和阿粉吃饭,在吃饭的时候和阿粉疯狂的吐槽面试官,说面试官问的问题都是些什么问题呀,我一个干了三四年的开发,也不说问点靠谱的,阿粉很好奇,问题问完基础的,一般不都是根据你自己的简历进行提问么?... 查看详情

新浪网面试官:说说java并发编程中countdownlatch原理与应用(代码片段)

前言控制并发流程的工具类,作用就是帮助我们程序员更容易的让线程之间合作,让线程之间相互配合来满足业务逻辑。比如让线程A等待线程B执行完毕后再执行等合作策略。控制并发流程的工具类主要有:简介背景CountDownLatch... 查看详情

面试官心理分析+面试题剖析:消息队列+redis缓存+分布式系统等

消息队列1、为什么使用消息队列?消息队列有什么优点和缺点?Kafka、ActiveMQ、RabbitMQ、RocketMQ都有什么优点和缺点?2、如何保证消息队列的高可用?3、如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?4、如何... 查看详情

带你整理面试过程中关于mybatis底层的相关知识

文章目录一、什么是Mybatis?二、Mybatis优缺点及使用场合三、#和$的区别是什么?四、mappers映射器对应的Dao接口的工作原理?五、Mybatis的一级和二级缓存六、Mybatis的Mapper接口调用要求七、resultMap和resultType八、Mybatis实... 查看详情

面试官:说说leftjoin和leftsemijoin两者有什么区别?(代码片段)

...程序员,主要是做java开发,有次到一家软件公司面试软件开发岗位,面试官问了他两个问题,其中有一个问题是这样的这样:说说leftjoin和leftsemijoin有什么区别?对于leftjoin张工在平时编写sql用得比较多... 查看详情

mybatis源码分析插件实现原理

MyBatis插件原理----从<plugins>解析开始本文分析一下MyBatis的插件实现原理,在此之前,如果对MyBatis插件不是很熟悉的朋友,可参看此文MyBatis7:MyBatis插件及示例----打印每条SQL语句及其执行时间,本文我以一个例子说明了MyBatis... 查看详情

最全java面试题及答案整理(2023最新版)

所有的面试题目都不是一成不变的,面试题目只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。前言面试前还是很有必要针对性的刷一些题,很多朋友的实战能力很强,但是理论比较薄弱,面试前不做准... 查看详情

面试官:有了解过reentrantlock的底层实现吗?说说看(代码片段)

我们可以了解到它是一个可重入锁,下面我们就一起看一下它的底层实现~构造函数我们在使用的时候,都是先new它,所以我们先看下它的构造函数,它主要有两个:publicReentrantLock()sync=newNonfairSync();publicReentrantLock(booleanfair)... 查看详情

面试官:说说mysql存储引擎原理,幸好我准备过(代码片段)

你知道的越多,不知道的就越多,业余的像一棵小草!你来,我们一起精进!你不来,我和你的竞争对手一起精进!编辑:业余草blog.csdn.net/weixin_46269257推荐:https://www.xttblog.com/?p=5263MySQL的数... 查看详情

面试官:说说string.intern()和常量池?不同jdk版本有什么区别?(代码片段)

0.Background在JAVA语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。8种基本类型的常量池... 查看详情

面试官:你说说js中实现继承有哪几种方法?

前言面试官:“你说说JavaScript中实现继承有哪几种方法?”紧张的萌新:“额,class中用extends实现继承,然后...没了...”面试官:“...”······大家好,我是CoderBin,想必绝大部分人一说继承就会想到类中的继承吧,但其实继... 查看详情

阿里二面,面试官:说说javacas原理?(代码片段)

声明:本文禁止转载1.什么是CAS?2.CAS基本原理3.CAS在Java语言中的应用4.CAS的问题4.1.典型ABA问题4.2.自旋开销问题4.3.只能保证单个变量的原子性5.有态度的总结在并发编程中我们都知道i++操作是非线程安全的,这... 查看详情