本文修改日期:2019年10月10日

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 是负责执行低层映射语句的内部对象。

标签: none

添加新评论