mybatis 插件执行原理

Interceptor

public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;

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

  default void setProperties(Properties properties) {
    // NOP
  }
}

简单介绍一下Interceptor 这个接口,里面有三个方法,作用分别是
intercept : 这个方法没有加default ,显然是mybatis 希望我们去实现的方法,这个方法 的参数 Invocation,通过这个对象能反射带哦用原
来对象的方法

plugin : target 是被拦截对象 ,可以看到,注意,这个方法其实mybatis 提供了默认的实现 ,而再 Plugin.wrap(target, this)里面的逻辑其
实非常的简单,其实就是为为拦截的对象生成了一个代理对象,为什么要生成代理对象呢? 其实也是非常容易理解,我么编写插件的
目的其实就是为了去增强方法,那么动态代理无疑是非常好的选择

setProperties:见名知意,设置属性,就是允许再这个方法里面进行属性的设置,方法再插件初始化的时候被调用
,如下

 private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
        interceptorInstance.setProperties(properties); //!!!!!!!!!!!!!!!!!!!!!
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

原理

如果以拦截Executor,那么就得就得从获取 SqlSession 说起
获取SqlSession 里面很关键的一步就是创建执行器 ------------Configruation # newExecutor

 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    //代码省略
      executor = new SimpleExecutor(this, transaction);
   
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

默认情况下是创建一个叫做SimpleExecutor 的执行器,那么创建完执行器后,就执行了一个非常眼熟的方法 pluginAll(executor),这个方法似乎跟 Interceptor 里面的 plugin 方法非常的像,

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

interceptors:是一个list ,存储着开发人员配置的Interceptor
没错,这里面会是进行 plugin方法的回调!!!

而plugin 前面说过,里面 mybatis 提供了默认的实现,就是创建代理对象,并且这里是再for循环里面进行代理,也就是说将代理对象返回出来,又进行了一层代理,一层一层又一层…

不论怎么代理,那么再执行真正的数据库请求的执行一定是会执行相对应的invoke方法

  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);
    }
  }

首先会根据签名过滤出需要拦截的方法,如果需要拦截,那么就会执行 Interceptor 的intercept()方法,完成拦截的逻辑

以上是完成Executor 的拦截,除此,mybatis 还支持 StatementHandler的拦截

 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;
  }

ParameterHandler 的拦截

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

ResultSetHandler 的拦截

  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;
  }

这三个其实与Executor大同小异,实现原理类似

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值