该系列文章针对 Mybatis 3.5.1 版本
在 mybatis 中允许针对 SQL 在执行前后进行扩展操作,而这些扩展操作也叫做插件。
在 Mybaits 中允许用插件来拦截的方法包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
通过插件可以实现 SQL 打印,分页插件等功能。
这时候就会延伸出一个问题:如果存在多个插件,这些插件的执行顺序是怎样的?
一、插件的执行顺序
插件执行顺序一共有两种
1、不同拦截对象的执行顺序
Mybatis 提供了对如下四种对象的拦截,分别是:Executor
、ParameterHandler
、ResultSetHandler
、StatementHandler
。针对这四种对象的拦截的执行顺序是固定的,因为 Mybatis 代码的执行流程是固定的。
以 SimpleExecutor#query
来说,这四种对象的执行代码如下:
如下述代码:
通过源码知道,执行顺序为:Executor` -> `StatementHandler` -> `ParameterHandler` -> `StatementHandler` -> `ResultSetHandler
。
虽然中间 StatementHandler
执行了多次,但是总的来说,执行顺序优先级从高到低为:Executor` -> `StatementHandler` -> `ParameterHandler` -> `ResultSetHandler
2、同种拦截对象的执行顺序
针对同种对象如果存在多种拦截器对象,其拦截顺序如何?
首先在前面的文章中,知道了插件功能的实现是通过代理的方式对原有的如:Executor
、ParameterHandler
等进行了代理增强,而经过代理后的原有的 Executor
、ParameterHandler
等对象会以如下方式存在:
如图,存在多个拦截器都是先进后出,针对代理模式来说来说,可以对被代理的原始对象的处理前后进行代码增强操作。
而那个拦截器优先执行,取决于在生成代理对象时的顺序,也就是包裹在最外层的插件(拦截器)优先执行。
来回顾一下代理对象生成时的逻辑然后结合mybatis-config.xml
配置文件的关于 <plugins>
的配置信息,即可知道相关的执行顺序。
来看代理生成代码InterceptorChain#pluginAll
,如下:
如上述代码所示,通过遍历 interceptors
List 列表对 target
对象进行包装(target 可以是 Executor or ParameterHandler等)。
也就是在 List 列表的最开始的 interceptor 插件最先被包裹在 target 对象外层,也就是如下图所示:
如图,同个目标对象的多个拦截器的执行顺序,取决于mybatis 加载配置后,拦截器在 List<Interceptor> interceptors
中的顺序。
再来回顾一下 <plugins>
解析的关键代码,如下:
如上图所示,在配置文件中越靠前的插件配置,在 interceptors List 列表中的位置自然越靠前,其执行顺序自然越靠后。
总结:同个对象的多个拦截器执行顺序根据配置文件 mybatis-config.xml
插件配置顺序有关,配置越靠前,执行顺序越靠后
二、通过代码验证插件执行顺序
2.1、验证不同对象拦截器的执行顺序
验证入口为 Executor#query
,准备查询接口,同时准备4个插件拦截器,并将四个插件拦截器配置到 mybatis-config.xml
配置文件 <plugins>
标签中(顺序无所谓)
2.1.1、查询接口忽略
2.1.2、四个插件拦截器
如上述定义了四个插件拦截器,分别对应四种对象的拦截器,以 Executor
拦截器为例 ,代码如下:
通过打印日志,查看执行顺序,其他拦截器相同
2.1.3、在配置文件中配置
如上述配置,为了验证配置顺序无关性,特地打乱其配置顺序。
2.1.4、执行结果
执行结果正如我们所预料的一样,执行顺序Mybatis 已经固化了。
2.2、验证多个同对象拦截器的执行顺序
以
Executor#query
为例
2.2.1、创建多个 Executor
拦截器,并拦截同一个方法 Executor#query
其中ExecutorQueryPlugin1
代码如下:
2.2.2、配置文件配置
如上述配置所示,拦截器配置顺序为 2,1,3
,通过上述结论可以得出,其执行顺序为:3,1,2----2,1,3
2.2.3、测试结果
控制台打印信息如下:
三、总结
Mybatis 插件的执行顺序有两种:
1、不同拦截对象执行顺序,如下:
Executor` -> `StatementHandler` -> `ParameterHandler` -> `ResultSetHandler
2、拦截相同对象执行顺序,取决于 mybatis-config.xml 中 <plugin> 配置顺序,越靠后,优先级越高。