MyBatis 插件(plugins)介绍


1、MyBatis插件

MyBatis允许用户在已映射语句执行过程中的某一点进行拦截调用。MyBatis使用插件来拦截的方法调用,故此MyBatis插件通常称为:Mybatis拦截器。默认情况下,MyBatis允许使用插件来拦截的对象包括下面的四个:

  • Executor
  • ParameterHandler
  • ResultSetHandler
  • StatementHandler

说明:Mybatis可以对这四个接口中所有的方法进行拦截。

Mybatis拦截器只能拦截四种类型的接口:Executor、StatementHandler、ParameterHandler和ResultSetHandler。这是在Mybatis的Configuration中写死了的。就是在下面的几个函数里面生成代理对象实现拦截的:

MyBatis入门 插件(Interceptor 拦截器)_拦截器

这四个类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。这些都是更低层的类和方法,所以使用插件的时候要特别当心。

1.1、MyBatis插件(代理类)示例图

MyBatis入门 插件(Interceptor 拦截器)_自定义_02

如上图所示:MyBatis系统最终会将插件包装成代理类,通过代理类来执行插件里面的功能。

2、MyBatis自定义插件的实现

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

Interceptor 接口的定义如下所示:

public interface Interceptor {
  //拦截器具体实现
  Object intercept(Invocation invocation) throws Throwable;
  //拦截器的代理类
  Object plugin(Object target);
  //添加属性
  void setProperties(Properties properties);
}


对于实现自己的Interceptor而言,有两个很重要的注解:

(1)@Intercepts用于表明当前的对象是一个Interceptor。其值是一个@Signature数组。代码如下所示:

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Intercepts
    {
        Signature[] value();
    }

(2)@Signature则表明要拦截的接口、方法以及对应的参数类型。代码如下所示:

@Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Signature {
      Class<?> type();

      String method();

      Class<?>[] args();
    }

下面来看一个自定义的简单Interceptor,出自MyBatis官方教程:

// ExamplePlugin.java
    @Intercepts({@Signature(
      type= Executor.class,
      method = "update",
      args = {MappedStatement.class,Object.class})})
    public class ExamplePlugin implements Interceptor {
      private Properties properties = new Properties();
      public Object intercept(Invocation invocation) throws Throwable {
        // implement pre processing if need
        Object returnObject = invocation.proceed();
        // implement post processing if need
        return returnObject;
      }
      public Object plugin(Object target) {
        return Plugin.wrap(target, this);
      }
      public void setProperties(Properties properties) {
        this.properties = properties;
      }
    }

代码分析:

在上面的源码中Plugin.wrap(),是当前拦截器(ExamplePlugin)的代理类。MyBatis通过这个代理类来实现拦截的功能。从这里也可以看出来,MyBatis插件和拦截器的关系:插件是拦截器的代理类。

<!-- mybatis-config.xml -->
    <plugins>
      <plugin interceptor="org.mybatis.example.ExamplePlugin">
        <property name="someProperty" value="100"/>
      </plugin>
    </plugins>


Mybatis在注册自定义的拦截器时,会先把对应拦截器下面的所有property通过Interceptor的setProperties方法注入给对应的拦截器。然后这个插件将会拦截在 Executor 实例中所有的 “update” 方法调用,这里的 Executor 是负责执行低层映射语句的内部对象。