说说MyBatis插件执行顺序(PageHelper 5 问题)

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

<property name="plugins">
    <array>
        <bean class="xxx.xxx.Interceptor1"/>
        <bean class="xxx.xxx.Interceptor2"/>
        <bean class="xxx.xxx.Interceptor3"/>
    </array>
</property>

    如,配置了三个插件,三个插件的拦截配置如下:

//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:

<property name="plugins">
    <array>
        <bean class="xxx.xxx.Interceptor1"/>
        <bean class="xxx.xxx.Interceptor2.1"/>
        <bean class="xxx.xxx.Interceptor3"/>
    </array>
</property>
    执行顺序变成了:
 
3 > 2.1 > 1 executor.query() > 1 > 2.1 > 3
    但是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的后边,变成:
<property name="plugins">
	<array>
	    <bean class="xxx.xxx.Interceptor2.1"/>
	    <bean class="xxx.xxx.Interceptor1"/>
	    <bean class="xxx.xxx.Interceptor3"/>
	</array>
</property>
    PageHelperV5的这种改变面上看主要是可以直接获得boundSql等对象,不用再次转换,其他的改变没有看,希望经验者多多指教!

猜你喜欢

转载自blog.csdn.net/mingjia1987/article/details/79665002