起源:https://www.cnblogs.com/chenp...

背景

对于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开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!