Mybatis 插件的运行原理

导读:本篇文章讲解 Mybatis 插件的运行原理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

核心对象

MyBatis 插件的运行是基于 JDK 动态代理 + 拦截器链实现

  • Interceptor 是拦截器,可以拦截 Executor, StatementHandle, ResultSetHandler, ParameterHandler 四个接口

    实际就是利用 JDK动态代理,生成对应的代理类实例,通过 InvocationHandler#invoke 实现拦截逻辑

  • InterceptorChain 是拦截器链,对象定义在 Configuration 类中, 实际是一个 List集合,元素就是 Interceptor接口的实现类

  • Invocation 是对方法、方法参数、执行对象和方法的执行的封装,就是对 InvocationHandler#invoke方法参数的封装

加载解析拦截器

mybatis-config.xml

<configuration>
    <plugins>
        <plugin interceptor="io.github.tangmonkmeat.MyInterceptor1" />
        <plugin interceptor="io.github.tangmonkmeat.MyInterceptor2"/>
    </plugins>
</configuration>

拦截器的解析是在 XMLConfigBuilder 对象的 parseConfiguration 方法中,

如果是 Mybatis ,则从 SqlSessionFactoryBuilder#build方法开始

如果是 springboot-mybatis-starter,则从 SqlSessionFactoryBean#buildSqlSessionFactory 开始

private void parseConfiguration(XNode root) {
    try {
        // issue #117 read properties first
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        // 开始加载解析插件的标签,添加到 Configuration
        pluginElement(root.evalNode("plugins"));

        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

拦截器(Interceptor)

Interceptor 接口配置文件中类需要实现的接口,可以添加属性,在方法执行前后添加自定义逻辑代码

代理类,通过 InvocationHandler#invoke 调用 拦截器的 intercept方法,实现拦截逻辑

@Intercepts({
    	// 被代理类的类型,和 需要代理的方法名及其参数类型
        @Signature(
                type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
        )
})
public class MyInterceptor1 implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截器逻辑
        
        // ...
        
        // 反射调用 被代理类的方法
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}

将拦截器配置添加 Configuration

创建拦截器、设置属性、添加到 Configuration 的拦截器链 InterceptorChain

InterceptorChain 实际是一个 List集合 private final List<Interceptor> interceptors = new ArrayList<>();

private void pluginElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // 拦截器全类名 io.github.tangmonkmeat.MyInterceptor1
      String interceptor = child.getStringAttribute("interceptor");
      // 获取配置属性, 如果interceptor标签没有其他属性,则为 null
      Properties properties = child.getChildrenAsProperties();
      // 根据全类名,创建拦截器实例
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
      // 设置拦截器的属性
      interceptorInstance.setProperties(properties);
      // 添加拦截器到 configuration 的拦截器链 InterceptorChain 中  
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

拦截器逻辑插入到四大核心接口

实际就是 利用 JDK的动态代理, 生成 核心接口实现类的代理类,

比如 SimpleExecutor,使用 Plugin 类 作为 InvocationHandler的实现类,

Plugin 持有 SimpleExcutor的引用,和 拦截器的引用,可以在 invoke方法中添加 拦截处理逻辑

Plugin (InvocationHandler的实现类)

public class Plugin implements InvocationHandler {

  // 被代理类
  private final Object target;
  // 拦截器
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;
  
  // ...
  
  // Configuration 会调用此方法,生成 代理类
  // 如果拦截器有多个,可以循环调用此方法,生成代理类的代理类。。。
  // 返回最外层的代理类,最内部的就是 被代理类
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          // InvocationHandler
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
    
  @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));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
}

添加拦截器

public class Configuration {
  // 拦截器链,实际就是一个 List集合
  protected final InterceptorChain interceptorChain = new InterceptorChain();

  // 参数处理
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    //创建参数处理对象
    ParameterHandler parameterHandler = mappedStatement.getLang().
    										createParameterHandler(mappedStatement, parameterObject, boundSql);
    //将拦截器链中的拦截器拦截动态代理中的参数处理方法执行,加入插件逻辑
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  // 结果集处理
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    //创建结果集处理对象
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    //将拦截器链中的拦截器拦截动态代理中的结果集处理方法执行,加入插件逻辑
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  // 数据库操作处理
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //创建数据库操作对象
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //将拦截器链中的拦截器拦截动态代理中的数据库操作方法执行,加入插件逻辑
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  // 执行器处理
  // SimpleExecutor 就是在这里生成的
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    //创建执行器
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 将拦截器链中的拦截器拦截动态代理中的执行器方法执行,加入插件逻辑
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}

附录

MyBatis 插件的运行原理是什么?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69688.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!