Mybatis插件原理

1、概述

  Mybatis允许使用插件拦截的方法调用包括:

  • Excetor
  • ParameterHandler
  • ResultHandler
  • StatementHandler

  

  上图是Mybatis框架的整体执行架构,Mybatis插件能够对四大对象接口进行拦截。

  • Executor : Mybatis执行器,是Mybatis调度核心,负责SQL语句的完成和查询缓存的维护
  • StatementHandler : 封装了JDBC Statement操作,负责对JDBC Statement的操作,如设置参数、将Statement结果集转换成list集合
  • ParameterHandler :负责将用户传递的参数转换成JDBC Statement所需要的参数
  • ResultSetHandler :负责将Java数据类型和JDBC数据类型之间的映射和转换

 2、插件实现原理

Executor代理对象

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

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

可拦截类对象都是通过pluginAll来实现的

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
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,
new Plugin(target, interceptor, signatureMap));
}
return target;
}

1、遍历所有拦截器,调用拦截器的plugin方法生成代理对象

2、注意:生成的对象又重新赋值给了target,所以如果有多个拦截器,生成的代理对象会被另一个代理对象所代理,形成一个代理链条,执行的时候,依次执行所有拦截器的拦截逻辑代码。

3、Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor,signatureMap)) 动态代理实现

3、拦截逻辑的执行

  由于真正去执行Executor、ParameterHandler、ResultSetHandler和StatementHandler类中的方法的对象是代理对象,所以在执行方法时,首先调用的时Plugin类,如下:

  

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
   //首先根据执行方法所属类获取拦截器中声明需要拦截的方法集合 Set
<Method> methods = signatureMap.get(method.getDeclaringClass());
   //判断当前方法需不需要执行拦截逻辑,需要的话,执行拦截逻辑方法(即Interceptor接口的intercept方法实现
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); } }
posted @ 2020-07-21 15:11  TPL  阅读(199)  评论(0编辑  收藏  举报