Mybatis插件原理

一、什么是Mybatis插件

MyBatis插件是MyBatis提供的一种机制,它可以在MyBatis内部的一些核心接口上拦截方法调用,从而实现对MyBatis核心功能的增强和扩展。
拦截器可以对MyBatis中的执行器(Executor)、语句处理器(StatementHandler)、参数处理器(ParameterHandler)和结果集处理器(ResultSetHandler)进行拦截,从而实现在执行SQL语句前后、分页、加密解密和打印日志等功能的实现。

二、自定义插件

首先自定义插件要实现 Mybatis 的 Interceptor 接口,然后实现这个接口的3个方法,最后在类上打上 @Intercepts 注解,并用 @Signature 注解标注要拦截的接口名称、方法名称以及参数列表。我们用 Executor 来举例

@Intercepts(
        {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
@Slf4j
public class ExecutorInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //增加自己的逻辑
        log.info("执行器-------自定义处理逻辑");
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}

plugin 方法统一都是这么写,setProperties 方法空着没关系,重点是 intercept 方法,增加自己的逻辑,最后调用 invocation.proceed() 就行了,其实 plugin 和 setProperties 不实现也没关系,因为 Interceptor 接口里有默认实现
Mybatis插件原理_第1张图片
其实只要实现 intercept 方法就可以了,像这样

@Intercepts(
        {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
@Slf4j
public class ExecutorInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //增加自己的逻辑
        log.info("执行器-------自定义处理逻辑");
        return invocation.proceed();
    }

}

类上面的注解,代表会拦截 Executor 的 query 方法,参数是这四个query方法MappedStatement, Object, RowBounds, ResultHandler,也就是这个
Mybatis插件原理_第2张图片然后就要把这个拦截器加到拦截器链里去,我这边用的SpringBoot,所以是这么配置的

@Configuration
public class MybatisConfig {

    @Bean
    public Interceptor[] interceptors() {
        ExecutorInterceptor executorInterceptor = new ExecutorInterceptor();
        return new Interceptor[]{executorInterceptor};
    }
}

三、源码解析

我们先看看四大对象(ExecutorStatementHandlerParameterHandlerResultSetHandler)在创建的时候,干了什么事,创建的方法都在 Configuration 类里
Mybatis插件原理_第3张图片
Mybatis插件原理_第4张图片我们发现,都有 interceptorChain.pluginAll 这个方法,都是传入什么对象,返回还是什么对象。
我们看看这个方法
Mybatis插件原理_第5张图片
可以看到 InterceptorChain 里有一个拦截器集合,pluginAll 方法就是遍历这个集合,并调用每个拦截器的 plugin 方法,而这个 plugin 方法就是接口 Interceptor 的默认方法
Mybatis插件原理_第6张图片
最终调用的是 Plugin 的 wrap 方法,我们再进去看下
Mybatis插件原理_第7张图片
可以看到这里是创建了代理类,而这个 Plugin 实现的正是 Java 动态代理机制的核心接口 InvocationHandler,这里很显然了,当调用被拦截的方法时,会先调用 Plugin 类的 invoke 方法,看下这个方法
Mybatis插件原理_第8张图片
可以看到最终是调用的拦截器的 intercept 方法,也就是我们自己实现的那段逻辑

四、总结

Mybatis的插件其实就是采用责任链机制,通过JDK动态代理来实现的。

  1. 在项目启动阶段,如果发现四大对象(ExecutorStatementHandlerParameterHandlerResultSetHandler)的方法有被拦截,就返回代理对象,否则就返回对象本身。
  2. 在调用sql阶段,如果调用四大对象的方法时,发现是代理对象,就执行 Plugin 的 invoke 方法(最终会执行拦截器的 intercept 方法),否则就执行原方法。

你可能感兴趣的:(Mybatis,源码学习,Java,mybatis,java,mysql)