Mybatis插件的原理

1.自定义插件

1.1创建Interceptor实现类

我们创建的拦截器必须要实现Interceptor接口,Interceptor接口的定义为


public interface Interceptor {

  // 执行拦截逻辑的方法 111
  Object intercept(Invocation invocation) throws Throwable;

  // 决定是否触发 intercept()方法
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  // 根据配置 初始化 Intercept 对象
  default void setProperties(Properties properties) {
    // NOP
  }
}

在MyBatis中Interceptor允许拦截的内容是

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)
@Intercepts({
        @Signature(
                type = Executor.class // 需要拦截的类型
                ,method = "query"	// 需要拦截的方法
// args 中指定 被拦截方法的 参数列表
                ,args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}
        ),
        @Signature(
                type = Executor.class
                ,method = "close"
                ,args = {boolean.class}
        )
})
public class TestInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("TestInterceptor	拦截之前	");
        invocation.proceed();
        System.out.println("TestInterceptor	拦截之后	");
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}
1.2 配置拦截器

创建好自定义的拦截器后,我们需要在全局配置文件中添加自定义插件的注册

 <plugin interceptor="com.kangping.intercepter.TestInterceptor">
           <property name="testProp" value="1000"/>
 </plugin>
2.插件实现原理
2.1初始化操作

首先我们来看下在全局配置文件加载解析的时候做了什么操作。
在这里插入图片描述
进入方法内部可以看到具体的解析操作

 private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        // 获取<plugin> 节点的 interceptor 属性的值
        String interceptor = child.getStringAttribute("interceptor");
        // 获取<plugin> 下的所有的properties子节点
        Properties properties = child.getChildrenAsProperties();
        // 获取 Interceptor 对象
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
        // 设置 interceptor的 属性
        interceptorInstance.setProperties(properties);
        // Configuration中记录 Interceptor
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

该方法用来解析全局配置文件中的plugins标签,然后对应的创建Interceptor对象,并且封装对应的 属性信息。最后调用了Configuration对象中的方法。
进入到configuration.addInterceptor(interceptorInstance)

 public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
  }

通过这个代码我们发现我们自定义的拦截器最终是保存在了InterceptorChain这个对象中。而InterceptorChain的定义为

/**
 * InterceptorChain 记录所有的拦截器
 * @author Clinton Begin
 */
public class InterceptorChain {

  // 保存所有的 Interceptor  也就我所有的插件是保存在 Interceptors 这个List集合中的
  private final List<Interceptor> interceptors = new ArrayList<>();

  //
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) { // 获取拦截器链中的所有拦截器
      target = interceptor.plugin(target); // 创建对应的拦截器的代理对象
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}
2.2 如何创建代理对象

在解析的时候创建了对应的Interceptor对象,并保存在了InterceptorChain中,那么这个拦截器是如 何和对应的目标对象进行关联的呢?首先拦截器可以拦截的对象是
Executor,ParameterHandler,ResultSetHandler,StatementHandler.那么我们来看下这四个对象在创建的时候又什么要注意的

2.2.1Executor

在这里插入图片描述
我们可以看到Executor在装饰完二级缓存后会通过pluginAll来创建Executor的代理对象。

public Object pluginAll(Object target) {
	for (Interceptor interceptor : interceptors) { // 获取拦截器链中的所有拦截器
		target = interceptor.plugin(target); // 创建对应的拦截器的代理对象
	}
	return target;
}

进入plugin方法中,我们会进入到

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

然后进入到MyBatis给我们提供的Plugin工具类的实现 wrap方法中。


  /**
   * 创建目标对象的代理对象
   *    目标对象 Executor  ParameterHandler  ResultSetHandler StatementHandler
   * @param target 目标对象
   * @param interceptor 拦截器
   * @return
   */
  public static Object wrap(Object target, Interceptor interceptor) {
    // 获取用户自定义 Interceptor中@Signature注解的信息
    // getSignatureMap 负责处理@Signature 注解
    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,
          new Plugin(target, interceptor, signatureMap));
    }
    // 否则原封不动的返回目标对象
    return target;
  }

Plugin中的各个方法的作用


/**
 * @author Clinton Begin
 */
public class Plugin implements InvocationHandler {

  private final Object target; // 目标对象
  private final Interceptor interceptor; // 拦截器
  private final Map<Class<?>, Set<Method>> signatureMap; // 记录 @Signature 注解的信息

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  /**
   * 创建目标对象的代理对象
   *    目标对象 Executor  ParameterHandler  ResultSetHandler StatementHandler
   * @param target 目标对象
   * @param interceptor 拦截器
   * @return
   */
  public static Object wrap(Object target, Interceptor interceptor) {
    // 获取用户自定义 Interceptor中@Signature注解的信息
    // getSignatureMap 负责处理@Signature 注解
    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,
          new Plugin(target, interceptor, signatureMap));
    }
    // 否则原封不动的返回目标对象
    return target;
  }

  /**
   * 代理对象方法被调用时执行的代码
   * @param proxy
   * @param method
   * @param args
   * @return
   * @throws Throwable
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 获取当前方法所在类或接口中,可被当前Interceptor拦截的方法
      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);
    }
  }

  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
      try {
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }

  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<>();
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }

}

2.2.2 StatementHandler

在获取StatementHandler的方法中

 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 注意,已经来到SQL处理的关键对象 StatementHandler >>
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 获取一个 Statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行查询
      return handler.query(stmt, resultHandler);
    } finally {
      // 用完就关闭
      closeStatement(stmt);
    }
  }

在进入newStatementHandler方法

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

可以看到statementHandler的代理对象

2.2.3 ParameterHandler

在上面步骤的RoutingStatementHandler方法中,我们来看看

 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPARED
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        // 创建 StatementHandler 的时候做了什么? >>
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

在newParameterHandler的步骤我们可以发现代理对象的创建

 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    // 植入插件逻辑(返回代理对象)
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

2.2.4 ResultSetHandler

在上面的newResultSetHandler()方法中,也可以看到ResultSetHander的代理对象

 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;
  }
2.3 执行流程

以Executor的query方法为例,当查询请求到来的时候,Executor的代理对象是如何处理拦截请求的 呢?我们来看下。当请求到了executor.query方法的时候

在这里插入图片描述
然后会执行Plugin的invoke方法

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 获取当前方法所在类或接口中,可被当前Interceptor拦截的方法
      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 会进入我们自定义的 TestInterceptor对象中

@Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("TestInterceptor	拦截之前	");
        invocation.proceed();
        System.out.println("TestInterceptor	拦截之后	");
    }

2.4 多拦截器

如果我们有多个自定义的拦截器,那么他的执行流程是怎么样的呢?比如我们创建了两个Interceptor 都是用来拦截 Executor 的query方法,一个是用来执行逻辑A 一个是用来执行逻辑B的。单个拦截器的执行流程

在这里插入图片描述
如果说对象被代理了多次,这里会继续调用下一个插件的逻辑,再走一次Plugin的invoke()方法。这里我们需要关注一下有多个插件的时候的运行顺序。
配置的顺序和执行的顺序是相反的。InterceptorChain的List是按照插件从上往下的顺序解析、添加 的。
而创建代理的时候也是按照list的顺序代理。执行的时候当然是从最后代理的对象开始。
在这里插入图片描述
这个我们可以通过实际的案例来得到验证,最后来总结下Interceptor的相关对象的作用

对象作用
Interceptor自定义插件需要实现接口,实现4个方法
InterceptChain配置的插件解析后会保存在Configuration的InterceptChain中
Plugin触发管理类,还可以用来创建代理对象
Invocation对被代理类进行包装,可以调用proceed()调用到被拦截的方法
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值