拦截器接口介绍

MyBatis 插件可以用来实现拦截器接口 Interceptor(org.apache.ibatis.plugin.Interceptor),在实现类中对拦截对象和方法进行处理。

先来看拦截器接口,了解该接口的每一个方法的作用和用法。Interceptor 接口代码如下。

public interface Interceptor {
    Object intercept(Invocation var1) throws Throwable;

    Object plugin(Object var1);

    void setProperties(Properties var1);
}

先从最简单的拦截器接口讲起。首先是 setProperties 方法,这个方法用来传递插件的参数,可以通过参数来改变插件的行为。参数值是如何传递进来的呢?要搞清楚这个问题,需要先看一下拦截器的配置方法。在 mybatis-config.xml 中,一般情况下,拦截器的配置如下。

<plugins>
    <plugin interceptor="tk.mybatis.simple.plugin.XXXInterceptor">
        <property name="prop1" value="value1"/>
        <property name="prop2" value="value2"/>
    </plugin>
</plugins>

在配置拦截器时,plugin 的 interceptor 属性为拦截器实现类的全限定名称,如果需要参数,可以在 plugin 标签内通过 property 标签进行配置,配置后的参数在拦截器初始化时会通过 setProperties 方法传递给拦截器。在拦截器中可以很方便地通过 Properties 取得配置的参数值。

再看第二个方法 plugin。这个方法的参数 target 就是拦截器要拦截的对象,该方法会在创建被拦截的接口实现类时被调用。该方法的实现很简单,只需要调用 MyBatis 提供的 Plugin(org.apache.ibatis.plugin.Plugin) 类的 wrap 静态方法就可以通过 Java 的动态代理拦截目标对象。这个接口方法通常的实现代码如下。

@Override
public Object plugin(Object target) {
    return Plugin.wrap(target, this);
}

Plugin.wrap 方法会自动判断拦截器的签名和被拦截对象的接口是否匹配,只有匹配的情况下才会使用动态代理拦截目标对象,因此在上面的实现方法中不必做额外的逻辑判断。

最后一个 intercept 方法是 MyBatis 运行时要执行的拦截方法。通过该方法的参数 invocation 可以得到很多有用的信息,该参数的常用方法如下。

public Object intercept(Invocation invocation) throws Throwable {
    Object target = invocation.getTarget();
    Method method = invocation.getMethod();
    Object[] args = invocation.getArgs();
    Object result = invocation.proceed();
    return result;
}

使用 getTarget() 方法可以获取当前被拦截的对象,使用 getMethod() 可以获取当前被拦截的方法,使用 getArgs() 方法可以返回被拦截方法中的参数。通过调用 invocation.proceed();可以执行被拦截对象真正的方法,proceed() 方法实际上执行了 method.invoke(target,args) 方法,上面的代码中没有做任何特殊处理,直接返回了执行的结果。

当配置多个拦截器时,MyBatis 会遍历所有拦截器,按顺序执行拦截器的 plugin 方法,被拦截的对象就会被层层代理。在执行拦截对象的方法时,会一层层地调用拦截器,拦截器通过 invocation.proceed() 调用下一层的方法,直到真正的方法被执行。方法执行的结果会从最里面开始向外一层层返回,所以如果存在按顺序配置的 A、B、C 三个签名相同的拦截器,MyBaits 会按照 C>B>A>target.proceed()>A>B>C 的顺序执行。如果 A、B、C 签名不同,就会按照 MyBatis 拦截对象的逻辑执行。