MyBatis插件执行顺序问题

之前因为觉得spring cache的使用比较麻烦,所以弄了两个简单的MyBatisCache自动缓存插件 ,在测试过程中,PageHelper插件的版本变化引出了插件执行顺序的问题:
起因:PageHelper v4.x和v5.x在实现上进行了改变,PageHelper主要是拦截Executor的query方法,为select语句添加物理分页语句,问题就出在query方法上,query方法有两个:

1
2
1,<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
2,<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

两个方法参数不一样,其中第一个方法是被第二个方法座位内部调用,所以一般mybatis的插件对于Executer的query拦截都是拦截的第二个方法,包括PageHelper在v5之前也都是这么拦截的,所以在通常情况下插件执行顺序是这样的:
比如插件配置如下:
1
2
3
4
5
6
7
<property name="plugins">
<array>
<bean class="xxx.xxx.Interceptor1"/>
<bean class="xxx.xxx.Interceptor2"/>
<bean class="xxx.xxx.Interceptor3"/>
</array>
</property>

如,配置了三个插件,三个插件的拦截配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
//Interceptor1:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
//Interceptor2:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
//Interceptor3:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})

三个插件的都是针对Executor类的四个参数的query方法进行拦截,这种情况下,拦截器的加载顺序是1、2、3,但是在interceptorChain.pluginAll()方法的层层代理处理后,拦截器的执行顺序变成了:
3拦截前处理 > 2拦截前处理 > 1拦截前处理 > executor.query() > 1拦截后处理 > 2拦截后处理 > 3拦截后处理

这个顺序在使用过多个拦截器的人应该比较清楚,具体的原理就不多说了,以上是通常情况,但是在PageHelperV5对拦截方法进行变动,他的拦截处理后,跳过了Executor的四个参数query方法的调用,直接调用6个参数的query方法,这样就会导致多个插件执行顺序上的问题:

接着按照上边的例子说,将Interceptor2换成Interceptor2.1:

1
2
3
4
5
6
7
<property name="plugins">
<array>
<bean class="xxx.xxx.Interceptor1"/>
<bean class="xxx.xxx.Interceptor2.1"/>
<bean class="xxx.xxx.Interceptor3"/>
</array>
</property>

执行顺序变成了:
1
3 > 2.1 > 1 executor.query() > 1 > 2.1 > 3

但是1根本就不会执行,实际的执行顺序是:
1
3 > 2.1 > executor.query(6参数) > 2.1 > 3

因为从2.1之后executor的四个参数的方法被跳过了,直接执行了6参数的query,所以Interceptor1没有拦截到,这就是问题所在。
对于PageHelperV5打破”拦截秩序“的问题,作者也做了说明,QueryInterceptor规范 ,针对Interceptor1的问题,PageHelperV5作者给出的解决办法:
1,修改Interceptor1的拦截策略,同时拦截4个和6个参数的query方法,这样就不再受“打破秩序者”的干扰;
2,让“打破秩序者”最后执行,调整Interceptor1拦截器的配置顺序,放到Interceptor2.1的后边,变成:

1
2
3
4
5
6
7
<property name="plugins">
<array>
<bean class="xxx.xxx.Interceptor2.1"/>
<bean class="xxx.xxx.Interceptor1"/>
<bean class="xxx.xxx.Interceptor3"/>
</array>
</property>

PageHelperV5的这种改变面上看主要是可以直接获得boundSql等对象,不用再次转换,其他的改变还没有看,希望经验者多多指教!