拦截器签名介绍

除了需要实现拦截器接口外,还需要给实现类配置以下的拦截器注解。

@Intercepts(org.apache.ibatis.plugin.Intercepts) 和签名注解 @Signature(org.apache.ibatis.plugin.Signature),这两个注解用来配置拦截器要拦截的接口的方法。

@Intercepts 注解中的属性是一个 @Signature(签名)数组,可以在同一个拦截器中同时拦截不同的接口和方法。

以拦截 ResultSetHandler 接口的 handleResultSets 方法为例,配置签名如下。

@Intercepts({
	@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultSetInterceptor implements Interceptor

@Signature 注解包含以下三个属性。

  • type:设置拦截的接口,可选值是前面提到的 4 个接口。

  • method:设置拦截接口中的方法名,可选值是前面 4 个接口对应的方法,需要和接口匹配。

  • args:设置拦截方法的参数类型数组,通过方法名和参数类型可以确定唯一一个方法。

由于 MyBatis 代码具体实现的原因,可以被拦截的 4 个接口中的方法并不是都可以被拦截的。下面将针对这 4 种接口,将可以被拦截的方法以及方法被调用的位置和对应的拦截器签名依次列举出来,大家可以根据方法调用的位置和方法提供的参数来选择想要拦截的方法。

Executor接口

Executor 接口包含以下几个方法。

  • int update(MappedStatement ms,Object parameter) throws SQLException 该方法会在所有的 INSERT、UPDATE、DELETE 执行时被调用,因此如果想要拦截这 3 类操作,可以拦截该方法。接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "update",
            args = {MappedStatement.class, Object.class}
    )
  • <E> List<E> query(
            MappedStatement ms,
            Object parameter,
            RowBounds rowBounds,
            ResultHandler resultHandler) throws SQLException

    该方法会在所有 SELECT 查询方法执行时被调用。通过这个接口参数可以获取很多有用的信息,因此这是最常被拦截的一个方法。使用该方法需要注意的是,虽然接口中还有一个参数更多的同名接口,但由于 MyBatis 的设计原因,这个参数多的接口不能被拦截。接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class,
                    RowBounds.class, Result Handler.class}
    )
  • <E> Cursor<E> queryCursor(
            MappedStatement ms,
            Object parameter,
            RowBounds rowBounds) throws SQLException

    该方法只有在查询的返回值类型为 Cursor 时被调用。接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "queryCursor",
            args = {MappedStatement.class, Object.class,
                    RowBounds.class}
    )
  • List <BatchResult> flushStatements() throws SQLException

    该方法只在通过 SqlSession 方法调用 flushStatements 方法或执行的接口方法中带有 @Flush 注解时才被调用,接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "flushStatements",
            args = {}
    )
  • void commit(boolean required) throws SQLException

    该方法只在通过 SqlSession 方法调用 commit 方法时才被调用,接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "commit",
            args = {boolean.class}
    )
  • void rollback(boolean required) throws SQLException

    该方法只在通过 SqlSession 方法调用 rollback 方法时才被调用,接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "rollback",
            args = {boolean.class}
    )
  • Transaction getTransaction()

    该方法只在通过 SqlSession 方法获取数据库连接时才被调用,接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "getTransaction",
            args = {}
    )
  • void close(boolean forceRollback)

    该方法只在延迟加载获取新的 Executor 后才会被执行,接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "close",
            args = {boolean.class}
    )
  • boolean isClosed()

    该方法只在延迟加载执行查询方法前被执行,接口方法对应的签名如下。

    @Signature(
            type = Executor.class,
            method = "isClosed",
            args = {}
    )

ParameterHandler接口

ParameterHandler 接口包含以下两个方法。

  • Object getParameterObject()

    该方法只在执行存储过程处理出参的时候被调用。接口方法对应的签名如下。

    @Signature(
            type = ParameterHandler.class,
            method = "getPara meterObject",
            args = {}
    )
  • void setParameters(PreparedStatement ps) throws SQLException

    该方法在所有数据库方法设置 SQL 参数时被调用。接口方法对应的签名如下。

    @Signature(
            type = ParameterHandler.class,
            method = "setParameters",
            args = {PreparedStatement.class}
    )

ResultSetHandler接口

ResultSetHandler 接口包含以下三个方法。

  • <E> List <E> handleResultSets(Statement stmt) throws SQLException;

    该方法会在除存储过程及返回值类型为 Cursor <T>(org.apache.ibatis.cursor.Cursor<T>) 以外的查询方法中被调用。接口方法对应的签名如下。

    @Signature(
            type = ResultSetHandler.class,
            method = "handle ResultSets",
            args = {Statement.class}
    )
  • <E> Cursor <E> handleCursorResultSets(Statement stmt) throws SQLException;

    该方法是 3.4.0 版本中新增加的,只会在返回值类型为 Cursor<T> 的查询方法中被调用,接口方法对应的签名如下。

    @Signature(
            type = ResultSetHandler.class,
            method = "handle CursorResultSets",
            args = {Statement.class}
    )
  • void handleOutputParameters(CallableStatement cs) throws SQLException;

    该方法只在使用存储过程处理出参时被调用,接口方法对应的签名如下。

    @Signature(
            type = ResultSetHandler.class,
            method = "handle OutputParameters",
            args = {CallableStatement.class}
    )

ResultSetHandler 接口的第一个方法对于拦截处理 MyBatis 的查询结果非常有用,并且由于这个接口被调用的位置在处理二级缓存之前,因此通过这种方式处理的结果可以执行二级缓存。在后面一节中会就该方法提供一个针对 Map 类型结果处理 key 值的插件。

StatementHandler接口

StatementHandler 接口包含以下几个方法。

  • Statement prepare(Connection connection,Integer transactionTimeout) throws SQLException;

    该方法会在数据库执行前被调用,优先于当前接口中的其他方法而被执行。接口方法对应的签名如下。

    @Signature(
            type = StatementHandler.class,
            method = "prepare",
            args = {Connection.class, Integer.class}
    )
  • void parameterize(Statement statement) throws SQLException;

    该方法在 prepare 方法之后执行,用于处理参数信息,接口方法对应的签名如下。

    @Signature(
            type = StatementHandler.class,
            method = "parameterize",
            args = {Statement.class}
    )
  • int batch(Statement statement) throws SQLException;

    在全局设置配置 defaultExecutorType="BATCH" 时,执行数据操作才会调用该方法,接口方法对应的签名如下。

    @Signature(
            type = StatementHandler.class,
            method = "batch",
            args = {Statement.class}
    )
  • <E> List<E> query(Statement statement,ResultHandler resultHandler) throws SQLException;

    执行 SELECT 方法时调用,接口方法对应的签名如下。

    @Signature(
            type = StatementHandler.class,
            method = "query",
            args = {Statement.class, ResultHandler.class}
    )
  • <E> Cursor<E> queryCursor(Statement statement) throws SQLException;

该方法是 3.4.0 版本中新增加的,只会在返回值类型为 Cursor<T> 的查询中被调用,接口方法对应的签名如下。

@Signature(
        type = StatementHandler.class,
        method = "queryCursor",
        args = {Statement.class}
)

在介绍了以上签名后,下面要动手实现两个简单的拦截器了。