作用

拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。Mybatis 拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动 Mybatis 固有的逻辑。打个比方,对于 Executor,Mybatis 中有几种实现:BatchExecutor、ReuseExecutor、SimpleExecutor 和 CachingExecutor。这个时候如果你觉得这几种实现对于 Executor 接口的 query 方法都不能满足你的要求,那怎么办呢?是要去改源码吗?当然不。我们可以建立一个Mybatis 拦截器用于拦截 Executor 接口的 query 方法,在拦截之后实现自己的 query 方法逻辑,之后可以选择是否继续执行原来的 query 方法。

声明拦截器

@Slf4j
@Component
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SlaveIntercepter implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
                                                         SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                                                         new DefaultReflectorFactory());
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
            log.info("mapper class:" + mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("被拦截方法执行之前,做的辅助服务······");
        Object proceed = invocation.proceed();
        System.out.println("被拦截方法执行之后,做的辅助服务······");

        return proceed;
    }
    @Override
    public Object plugin(Object target) {
        log.info(">>>>>>>:plugin");
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
        log.info(">>>>>>>:setProperties");

    }
}

分析

@Interceptor

其值是一个@Signature 数组。@Intercepts 用于表明当前的对象是一个 Interceptor

@Signature

表明要拦截的接口、方法以及对应的参数类型。

  • type-要拦截的接口
    可以拦截的接口只有四种 Executor.class,StatementHandler.class,ParameterHandler.class 和 ResultSetHandler.class。

    • Executor: 拦截执行器的方法
    • ParameterHandler: 拦截参数的处理
    • ResultSetHandler: 拦截结果集的处理
    • StatementHandler: 拦截Sql语法构建的处理
  • method-需要拦截的方法,存在于要拦截的接口之中
    点击进入上述接口,可查看存在的方法,根据自己的需求设置method

  • args-被拦截的方法所需要的参数

plugin方法

plugin 方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用 intercept 方法,当然也可以调用其他方法,这点将在后文讲解。

对于 plugin 方法而言,其实 Mybatis 已经为我们提供了一个实现。Mybatis 中有一个叫做Plugin 的类,里面有一个静态方法 wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。

在wrap()方法中,我们可以看出,在 signatureMap 拿到了需要拦截的接口,根据 target 的类型和 signatureMap 得到所有的接口,如果接口的数量大于0,就返回一个代理对象,否则直接返回原对象 target。

setProperties

setProperties方法是用于在 Mybatis 配置文件中指定一些属性的。

intercept

intercept方法就是要进行拦截的时候要执行的方法。

invocation

  • target:代理对象

  • method:被监控方法对象

  • args:当前被监控方法运行时需要的实参