天天看点

MyBatis自定义拦截器插件前言自定义插件插件执行顺序

前言

        MyBatis自身提供了接口,支持在映射语句的某一点进行拦截做一些处理。所以在我们使用mybatis这个框架来做一些数据的持久化方面的操作的时候,有时候可能根据业务需要,在执行一次操作的过程中,做一些定制化操作。

      在我以往开发的项目中,目前用到的也就是:

      1. 大SQL调用拦截,当然了,这个涉及到SQL解析并且需要统计一下本次SQL执行结果的行数来分析,需要在当前查询操作上多执行一次结果行数统计的SQL查询,所以如果实在必须的话,可以做这种操作,否则JDBC配置中有maxRows属性可以限制查询结果的最大行数,来避免SQL执行结果集过大撑爆JVM拖垮业务系统的情况发生。

      2.SQL操作日志的采集。有时候需要采集SQL信息的日志进行分析,可以考虑。如果不需要的话,用阿里的连接池这些日志打印的也很清晰了。

      3. 分页。这个嘛,个人推荐用github的PageHelper插件就挺好用。当然了,不好的一点, PageHelper在解析及拼接SQL过程中的处理,使用了自定义的SqlSource。如果有其它插件或自定义插件也做了类似处理,这个兼容性就不好说了。

      4.拦截SQL语句做其它调整处理。这个嘛就没啥说了,都是业务需要。

我目前用过的也就这几种,其它就不多说了。

       好了,上面说了一堆废话,接下来说重点。

自定义插件

配置

这个多嘴再提一句,mybatis的基于xml的插件配置。基于javaConfig的配置写法,这里暂不表了。

<!-- mybatis-config.xml -->
    <!-- 配置插件 -->
    <plugins>
        <!-- 指定插件类路径 -->
        <plugin interceptor="com.xuxd.mybatis.plugin.demo.CustomPlugin">
            <!-- 这里是插件需要的属性值配置 -->
            <property name="k" value="v"/>
            <property name="k" value="v"/>
            <property name="k" value="v"/>
        </plugin>
    </plugins>
           

不管第三方插件还是自定义插件都是这样配置的,反正都是插件。如果是自定义插件需要自定义插件属性配置的话,可以配置在上面的<property name="" value=""/>这里,K-V格式,这里的属性配置会调用插件的属性设置方法设置进去,具体看下面的代码说明。

拦截的接口

mybatis主要提供了下面4个接口支持拦截:

1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

2. ParameterHandler (getParameterObject, setParameters)

3. StatementHandler (prepare, parameterize, batch, update, query)

4. ResultSetHandler (handleResultSets, handleOutputParameters) 

上面括号内是该接口允许拦截的方法,关于这4个接口执行时间段按我自已的理解来解释下,下面看图,有不正确不合适的地方也请及时指正:

MyBatis自定义拦截器插件前言自定义插件插件执行顺序

代码实现

首先,需要实现Interceptor接口,类路径如下:

org.apache.ibatis.plugin.Interceptor
           

就3个方法需要实现,我用一个示例加上注释说明一下:

package com.xuxd.mybatis.plugin.demo;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Statement;
import java.util.Properties;

/**
 * Created by dong on 2019/2/22.
 */
@Intercepts({//注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这个拦截器
        @Signature(
                type = ResultSetHandler.class,//这是指拦截哪个接口
                method = "handleResultSets", //这个接口内的哪个方法名,不要拼错了
                args = {Statement.class})//这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的
        ,
        @Signature(type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class CustomPlugin implements Interceptor {
    // 这里是每次执行操作的时候,都会进行这个拦截器的方法内
    public Object intercept(Invocation invocation) throws Throwable {
        //TODO:自已的业务处理
        return invocation.proceed();
    }

    // 主要是为了把这个拦截器生成一个代理放到拦截器链中
    public Object plugin(Object target) {
        //官方推荐写法
        return Plugin.wrap(target, this);
    }

    // 插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来
    public void setProperties(Properties properties) {

    }
}
           

写法就看上面的代码示例与注释了,另外有几个地方,可能有迷惑,这里就顺便提一下:

1. @signature,这个东东如果只需要对一个地方拦截,就写一个就行,我上面是为了说明

2. 注意一下plugin这个方法,如果还没明白啥意思,就按示例写就行了。

插件执行顺序

这也可能是个让迷惑的地方,有时候配置了多个插件,哪个先被执行,哪个后被执行,这里也简单说明下

如下插件配置:

<!-- mybatis-config.xml -->
    <plugins>
        <plugin interceptor="com.xuxd.mybatis.plugin.demo.CustomPlugin1">
        </plugin>
        <plugin interceptor="com.xuxd.mybatis.plugin.demo.CustomPlugin2">
        </plugin>
        <plugin interceptor="com.xuxd.mybatis.plugin.demo.CustomPlugin3">
        </plugin>
    </plugins>
           

看下插件的配置顺序是CustomPlugin1,CustomPlugin2,CustomPlugin3。在执行拦截的时候,拦截的顺序是CustomPlugin3,CustomPlugin2,CustomPlugin1,这里指的是调用插件的intercept方法的顺序,在配置文件中配置的越靠后,越先被调用。

但其实插件的plugin方法的调用顺序是CustomPlugin1,CustomPlugin2,CustomPlugin3。

解释下原因:

其实mybatis的拦截器插件采用的是责任链模式,通过生成的代理对象一个一个调用下一个拦截器。所以在xml中配置的最靠前,会最先被调用pulgin创建为一个代理对象,然后解析第二个第三个来创建,它的模式大约可以这样理解:

MyBatis自定义拦截器插件前言自定义插件插件执行顺序

最先被调用plugin的插件,在最里面,调用interceptor方法的时候,代理对象在外层,一层一层解开往里面调。意思嘛就是这么个意思,表达的对不对也就这样了。

继续阅读