Mybatis 插件和动态代理

本次我们学习mybatis的插件,重点是理解他和动态代理的关系

动态代理简单回顾

上节我们已经详细的了解动态代理;主要是通过定义接口,通过

newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
                                          InvocationHandler h)

方法,创建一个接口的代理。interfaces 表示代理也需要遵守的协议,还有h是实现
InvocationHandler协议的实例,其中有一个方法invoke,也是逻辑增强或者类功能增强的地方。具体请参考:使用JDK API实现动态代理和源码分析
下面我们正式分析mybatis的插件

Mybatis 插件原理分析

@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class TenantInterceptor implements Interceptor {

我们最常见的插件应该就是这样的,首先实现Interceptor方法接口;

 Object intercept(Invocation invocation) throws Throwable;

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

我们关注上面上面这两个方法。
首先我们给出答案,intercept方式是逻辑增强的方法,plugin是创建代理的方法。
但是直接看好像和我们动态代理没有关系

生成代理

我们从 return Plugin.wrap(target, this);方法开始,也就是Interceptor 的第二个方法,在wrap方法中创建了代理

public static Object wrap(Object target, Interceptor interceptor) { // 1
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance( // 2
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
  1. 方法的第一个参数object就是需要增强的对象,第二个就是Interceptor,因为Interceptor的intercept有增强逻辑,例如修改SQL,分页插件等等,是我们生成代理需要的。
  2. 正式的生成代理对象,其中用到了PluginPlugin类一定实现InvocationHandler接口。

逻辑增强 InvocationHandler

还是在Plugin中的invoke方法,也就是InvocationHandler接口方法。

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));//1
      }
      return method.invoke(target, args);//2
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
  1. 我们实现的Interceptor实例的interceptor方法生效了,也就是逻辑增强
  2. 被代理类的方法执行

代理创建的触发时机

在Mybatis中重要的接口ExecutorStatementHandler,其中的方法逐层调用,都是在为执行SQL配置环境,其中StatementHandler就是。

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

上面是通过configuration配置类,创建StatementHandler,下面是具体的代码。

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler c= new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 1
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler; // 2
  }
  1. 创建StatementHandler
  2. 使用interceptorChain增强statementHandler
    也就是每次我们通过SqlSession获取Mapper,在Mapper上调用方法执行SQL时,都会触发interceptorChain增强对象。
    interceptorChain是在configuration中存储的,就是插件集合
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target); // 1
    }
    return target;
  }
  1. pluginAll方法,具体的增加就是创建调用我们编写插件的plugin方法,创建代理对象,例如
public Object plugin(Object target) {
      return Plugin.wrap(target,this); // 创建实例
  }

总结

插件就是在调用Mapper方法时,通过动态代理对Mybatis的某些接口增强,通过Interceptor的 @Signature注解指定需要增强的接口方法.

还有很多细节,欢迎大家讨论学习

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章