zoukankan      html  css  js  c++  java
  • 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); } }
  • 相关阅读:
    MVC应用程序使用Entity Framework
    @Styles的nameSpace是什么
    创建第一个MVC应用程序
    计算DataTable某列的值(SUM)
    DropDownList 控件的SelectedIndexChanged事件触发不了
    在类中使用Response.Redirect()方法
    控制某个panel的display样式
    获取指定日期下个月份的第一天
    字符串如何还原为中文
    判断字符串中包含3个连续(升、降)或相同的数字
  • 原文地址:https://www.cnblogs.com/TripL/p/13355188.html
Copyright © 2011-2022 走看看