实际上个各种类型的插件都大差不差,基本都是使用拦截器实现,找到自定义的拦截器,将其加入拦截器列表,在执行某些方法之前先执行拦截器列表里面的方法,Mybtais亦是如此。
注册插件
首先看看如何向Mybatis注册一个插件。
如上图所示,向Mybtais注册插件只需要在主配置文件的plugin标签中声明对应的自定义插件类即可。
读过前几篇文章的朋友应该了解,Mybtais的配置文件信息都是解析后存放在Configuration类中,插件信息也不例外。 我们从XMLConfigBuilder的源码可以看到解析plugin的代码
解析完plugin之后会调用configuration的addInterceptor()方法。下来我们看看addInterceptor()方法,如下所示,addInterceptor()方法将解析完成的插件信息放入interceptorChain中:
接下来我们看看interceptorChain的定义:
实际上在看到Chain这个单词就应该明白这是一条拦截器链,那么一般情况下链的存储都是由集合存储,接下来我们看看InterceptorChain的具体定义:
如上所示,该类中维护一个Interceptor的List,并且提供pluginAll()方法遍历执行该链。
至此Mybatis插件的存储已完成,即注册插件的过程已结束。
插件执行过程
上面我们看到了Mybatis的插件注册过程,实际上也提到了插件的运行最终会调用pluginAll()方法,遍历执行所有的插件。
暂时抛开这点,用户可以向Mybtais中注册插件,那么插件可以拦截到那些信息尼。前面的文章中我们提到过Configuration类的作用还可以作为Executor、StatementHandler、ResultSetHandler以及ParamterHandler的工厂类。接下来我们看看这几个实例的创建:
如上所示,在这些对象创建完之后都会调用pluginAll()方法。 前面我们也看到了pluginAll()方法的具体实现。接下来我们看看pluginAll()方法遍历的成员变量Interceptor。
如图所示,所有的自定义插件都必须实现Interceptor接口。Interceptor接口中定义了3个方法,intercept()方法用于定义拦截逻辑,该方法会在目标方法调用时执行。plugin()方法用于创建Executor、ParameterHandler、ResultSetHandler或StatementHandler的代理对象,该方法的参数即为Executor、ParameterHandler、ResultSetHandler或StatementHandler组件的实例。setProperties()方法用于设置插件的属性值。需要注意的是,intercept()接收一个Invocation对象作为参数,Invocation对象中封装了目标对象的方法及参数信息。Invocation类的实现代码如下:
如上面的代码所示,Invocation类中封装了目标对象、目标方法及参数信息,我们可以通过Invocation对象获取目标对象(Executor、ParameterHandler、ResultSetHandler或StatementHandler)的所有信息。另外,Invocation类中提供了一个proceed()方法,该方法用于执行目标方法的逻辑。所以在自定义插件类中,拦截逻辑执行完毕后一般都需要调用proceed()方法执行目标方法的原有逻辑。
我们也可以从Mybtais的test类中看出自定义插件需要实现Invocation类,如下所示
综上,我们通过实现Interceptor接口,并且在Mybtais的plugin组件中注册自定义实现类,便可以自定义一个Mybatis的插件,Mybtais的插件可以拦截到Executor、StatementHandler、ResultSetHandler以及ParamterHandler的相关信息,在日常开发中,我们可以根据需求对Mybtais进行定制化的插件开发,如慢Sql统计,只需要拦截StatmentHandler对应的query等方法,在前后加上时间戳,便可以轻松拦截到Sql执行耗时统计。