5分钟!彻底搞懂MyBatis插件+PageHelper原理

前言提到插件,相信大家都知道,插件的存在主要是用来改变或者增强原有的功能,MyBatis中也一样 。然而如果我们对MyBatis的工作原理不是很清楚的话,最好不要轻易使用插件,否则的话如果因为使用插件导致了底层工作逻辑被改变,很可能会出现很多意料之外的问题 。
本文主要会介绍MyBatis插件的使用及其实现原理,相信读完本文,我们也可以写出自己的PageHelper分页插件了 。
MyBatis中插件是如何实现的在MyBatis中插件式通过拦截器来实现的,那么既然是通过拦截器来实现的,就会有一个问题,哪些对象才允许被拦截呢?
之前提到,真正执行Sql的是四大对象:Executor,StatementHandler,ParameterHandler,ResultSetHandler 。而MyBatis的插件正是基于拦截这四大对象来实现的 。需要注意的是,虽然我们可以拦截这四大对象,但是并不是这四大对象中的所有方法都能被拦截,下面就是官网提供的可拦截的对象和方法汇总:

5分钟!彻底搞懂MyBatis插件+PageHelper原理

文章插图
 
MyBatis插件的使用首先我们先来通过一个例子来看看如何使用插件 。
1、首先建立一个MyPlugin实现接口Interceptor,然后重写其中的三个方法(注意,这里必须要实现Interceptor接口,否则无法被拦截) 。
package com.lonelyWolf.mybatis.plugin;import org.Apache.ibatis.executor.Executor;import org.apache.ibatis.mApping.MappedStatement;import org.apache.ibatis.plugin.*;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import JAVA.util.Properties;@Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})})public class MyPlugin implements Interceptor {/*** 这个方法会直接覆盖原有方法* @param invocation* @return* @throws Throwable*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("成功拦截了Executor的query方法,在这里我可以做点什么");return invocation.proceed();//调用原方法}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target,this);//把被拦截对象生成一个代理对象}@Overridepublic void setProperties(Properties properties) {//可以自定义一些属性System.out.println("自定义属性:userName->" + properties.getProperty("userName"));}}@Intercepts是声明当前类是一个拦截器,后面的@Signature是标识需要拦截的方法签名,通过以下三个参数来确定
  • type:被拦截的类名 。
  • method:被拦截的方法名
  • args:标注方法的参数类型
2、我们还需要在mybatis-config中配置好插件 。
<plugins><plugin interceptor="com.lonelyWolf.mybatis.plugin.MyPlugin"><property name="userName" value=https://www.isolves.com/it/sjk/bk/2020-10-14/"张三"/>这里如果配置了property属性,那么我们可以在setProperties获取到 。
完成以上两步,我们就完成了一个插件的配置了,接下来我们运行一下:
5分钟!彻底搞懂MyBatis插件+PageHelper原理

文章插图
 
可以看到,setProperties方法在加载配置文件阶段就会被执行了 。
MyBatis插件实现原理接下来让我们分析一下从插件的加载到初始化到运行整个过程的实现原理 。
插件的加载既然插件需要在配置文件中进行配置,那么肯定就需要进行解析,我们看看插件式如何被解析的 。我们进入XMLConfigBuilder类看看
5分钟!彻底搞懂MyBatis插件+PageHelper原理

文章插图
 
解析出来之后会将插件存入InterceptorChain对象的list属性
5分钟!彻底搞懂MyBatis插件+PageHelper原理

文章插图
 
看到InterceptorChain我们是不是可以联想到,MyBatis的插件就是通过责任链模式实现的 。
插件如何进行拦截既然插件类已经被加载到配置文件了,那么接下来就有一个问题了,插件类何时会被拦截我们需要拦截的对象呢?
其实插件的拦截是和对象有关的,不同的对象进行拦截的时间也会不一致,接下来我们就逐一分析一下 。
拦截Executor对象我们知道,SqlSession对象是通过openSession()方法返回的,而Executor又是属于SqlSession内部对象,所以让我们跟随openSession方法去看一下Executor对象的初始化过程 。
5分钟!彻底搞懂MyBatis插件+PageHelper原理

文章插图
 
可以看到,当初始化完成Executor之后,会调用interceptorChain的pluginAll方法,pluginAll方法本身非常简单,就是把我们存到list中的插件进行循环,并调用Interceptor对象的plugin方法:


推荐阅读