之前因为觉得spring cache的使用比较麻烦,所以弄了两个简单的MyBatisCache自动缓存插件 ,在测试过程中,PageHelper插件的版本变化引出了插件执行顺序的问题:

    起因:PageHelper v4.x和v5.x在实现上进行了改变,PageHelper主要是拦截Executor的query方法,为select语句添加物理分页语句,问题就出在query方法上,query方法有两个:

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

    两个方法参数不一样,其中第一个方法是被第二个方法座位内部调用,所以一般mybatis的插件对于Executer的query拦截都是拦截的第二个方法,包括PageHelper在v5之前也都是这么拦截的,所以在通常情况下插件执行顺序是这样的:
    比如插件配置如下:

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

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

    1. //Interceptor1:
    2. @Intercepts({
    3. @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    4. })
    5. //Interceptor2:
    6. @Intercepts({
    7. @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    8. })
    9. //Interceptor3:
    10. @Intercepts({
    11. @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    12. })

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

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

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

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

    PageHelperV5的这种改变面上看主要是可以直接获得boundSql等对象,不用再次转换,其他的改变还没有看