1、MyBatis插件

MyBatis允許用戶在已映射語句執行過程中的某一點進行攔截調用。MyBatis使用插件來攔截的方法調用,故此MyBatis插件通常稱為:Mybatis攔截器。默認情況下,MyBatis允許使用插件來攔截的對象包括下面的四個:

  • Executor
  • ParameterHandler
  • ResultSetHandler
  • StatementHandler

說明:Mybatis可以對這四個接口中所有的方法進行攔截。

Mybatis攔截器只能攔截四種類型的接口:Executor、StatementHandler、ParameterHandler和ResultSetHandler。這是在Mybatis的Configuration中寫死了的。就是在下面的幾個函數里面生成代理對象實現攔截的:

mybatis-interceptor-class.png

這四個類中方法的細節可以通過查看每個方法的簽名來發現,或者直接查看 MyBatis 發行包中的源代碼。如果你想做的不僅僅是監控方法的調用,那么你最好相當了解要重寫的方法的行為。因為如果在試圖修改或重寫已有方法的行為的時候,你很可能在破壞 MyBatis 的核心模塊。這些都是更低層的類和方法,所以使用插件的時候要特別當心。

1.1、MyBatis插件(代理類)示例圖

mybatis-proxy.png

如上圖所示: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 是負責執行低層映射語句的內部對象。