mybatis拦截器执行顺序配置_springmvc拦截器执行顺序

mybatis拦截器执行顺序配置_springmvc拦截器执行顺序1.原始jdbc工作流程原始jdbc工作流程以查询为例1加载驱动Class.forName(Driver.class.getName())2建立数据库连接Connectionroot=DriverManager.getConnection(“xx”,“xx”,“xx”)3预编译sql语句PreparedStatementpreparedStatement=root.prepareStatement(sql)4占位符参数赋值preparedSt

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

1.原始 jdbc 工作流程

原始 jdbc 工作流程 以查询为例

1.1 加载驱动

Class.forName(Driver.class.getName())

1.2 建立数据库连接

Connection root = DriverManager.getConnection(“xx”, “xx”, “xx”)

1.3 预编译sql语句

PreparedStatement preparedStatement = root.prepareStatement(sql)

1.4 占位符参数赋值

preparedStatement.setString(1,“1”); PS:下标从1开始

1.5 执行sql

1.6 返回结果集

ResultSet resultSet = preparedStatement.executeQuery()

1.7 关闭数据库连接

xxx…close()

2.源码探究 mybatis 工作流程

通过jdbc 的工作流程可以看到大致分为:

预编译sql语句,处理参数,执行sql语句,封装结果集

同样 mybatis 工作流程大致也是这样的。但是mybatis 在初始化封装 MappedStatement 对象的时候就已经完成了预编译。

大致分为:选择执行器,处理参数,执行sql语句,封装结果集
对应工作的mybatis 四大对象分别为:
Executor ParameterHandler StatementHandler ResultSetHandler
非常相似,因为mybatis 底层就是封装的 jdbc

执行器

类图
类图

1.选择执行器

mybatis 官网中 也有价绍,在mybatis 初始化的时候可以在配置文件的settings节点配置 defaultExecutorType 类型 ,默认的执行器为SIMPLE 还有另外两个即REUSE,BATCH。
区别:以批量插入为例

SIMPLE 每执行一次update操作,就开启一个Statement对象,用完立刻关闭Statement对象。

源码:SimpleExecutor

  @Override
  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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally { 
   
      closeStatement(stmt);
    }
  }

REUSE 每执行一次update操作,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象

源码:ReuseExecutor

  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { 
   
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  }

BATCH 每执行一次update操作,就缓存一个Statement对象,然后统一执行

源码:BatchExecutor

  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException { 
   
    Statement stmt = null;
    try { 
   
      flushStatements();
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      return handler.query(stmt, resultHandler);
    } finally { 
   
      closeStatement(stmt);
    }
  }

2 实例化执行器

在通过 SqlSessionFactory 实例化 SqlSession 的时候完成了执行器的初始化

SqlSession sqlSession = sqlSessionFactory.openSession()

  //默认的SqlSessionFactory 
  public SqlSession openSession() { 
   
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 
   
    Transaction tx = null;
    try { 
   
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) { 
   
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
    } finally { 
   
      ErrorContext.instance().reset();
    }
  }

初始化数据库信息

Environment environment = configuration.getEnvironment();

初始化事务信息

TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

主要的一行

final Executor executor = configuration.newExecutor(tx, execType);

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

3 通过执行器选择执行方法

当我们进行查询的时候,会通过命名空间+方法名 封装成一个 id ,去MappedStatement 集合里面获取到当前的 MappedStatement 对象,并开始通过执行器开始执行查询方法,源码:DefaultSqlSession

  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { 
   
    try { 
   
      MappedStatement ms = configuration.getMappedStatement(statement);
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) { 
   
      throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
    } finally { 
   
      ErrorContext.instance().reset();
    }
  }

3.通过mybatis 工作流程 窥探拦截器执行顺序

调用拦截器

executor.query(ms, wrapCollection(parameter), rowBounds, handler);

这一行是通过执行器的代理对象 去执行query的方法。
官网提供的拦截器插件文档
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
如果命中到拦截器就会执行拦截器的拦截方法,如果有条件限制 要么放行,要么执行拦截逻辑,代理对象是如何生成的呢?

创建代理对象

1 创建 Executor 代理对象

最终会进入到两个分支BaseExecutor 和 CachingExecutor
BaseExecutor 有三个实现类
CachingExecutor 内部维护了一个 Executor接口,在执行query方法的时候会再次进入 BaseExecutor 的 query 方法里面
在这里插入图片描述

BaseExecutor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)

这个方法里面有一个去查询的方法

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

到这个方法就是上面提到的不同执行器的 doQuery 方法
在这里插入图片描述
以SimpleExecutor 的 doQuery 方法为例

  @Override
  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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally { 
   
      closeStatement(stmt);
    }
  }

而在我们实例化执行器的时候 ,提到过这样一行代码

final Executor executor = configuration.newExecutor(tx, execType);

在这个newExecutor 里面去实例化了一个 执行器,并且

executor = (Executor) interceptorChain.pluginAll(executor);

会生成 Executor 的代理对象(在初始化的mybatis 环境的时候就已经将自定义的拦截器全部添加到一个内部维护的集合里面去了)

2 创建 StatementHandler 代理对象

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

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

先创建 一种 RoutingStatementHandler 的实例

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 
   
    switch (ms.getStatementType()) { 
   
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        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());
    }
  }

3 创建 ParameterHandler 代理对象

三个分支里面全部都有这样的一行代码

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

4 创建 ResultSetHandler 代理对象

三个分支里面全部都有这样的一行代码

this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  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;
  }

调用拦截器

官网提供的拦截器插件文档
StatementHandler (prepare, parameterize, batch, update, query)
以SimpleExecutor 的 doQuery 方法为例

  @Override
  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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally { 
   
      closeStatement(stmt);
    }
  }

stmt = prepareStatement(handler, ms.getStatementLog());

在这里插入图片描述

RoutingStatementHandler

  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { 
   
    return delegate.prepare(connection, transactionTimeout);
  }

再调用 BaseStatementHandler 的 prepare 方法
再回来 执行拦截器拦截的StatementHandler 的 prepare 方法,要么放行,要么执行拦截逻辑。再回来执行代码的下一行

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { 
   
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

即 StatementHandler handler.parameterize(stmt);又会拦截

调用拦截器

即 StatementHandler handler.parameterize(stmt);

一共四种策略
在这里插入图片描述

CallableStatementHandler #parameterize

  public void parameterize(Statement statement) throws SQLException { 
   
    registerOutputParameters((CallableStatement) statement);
    parameterHandler.setParameters((CallableStatement) statement);
  }

官网提供的拦截器插件文档
ParameterHandler (getParameterObject, setParameters)

执行 CallableStatementHandler 类型的 setParameters 方法的拦截器

PreparedStatementHandler #parameterize

  @Override
  public void parameterize(Statement statement) throws SQLException { 
   
    parameterHandler.setParameters((PreparedStatement) statement);
  }

执行 PreparedStatementHandler 类型的 setParameters 方法的拦截器

RoutingStatementHandler #parameterize

  @Override
  public void parameterize(Statement statement) throws SQLException { 
   
    delegate.parameterize(statement);
  }

通过路由到某一种StatementHandler 类型的 setParameters 方法的拦截器

SimpleStatementHandler #parameterize

  @Override
  public void parameterize(Statement statement) { 
   
    // N/A
  }

此类型的 setParameters 无拦截器工作

调用拦截器

回到上面的 doQuery 方法的最后一行

return handler.query(stmt, resultHandler);

官网提供的拦截器插件文档
StatementHandler (prepare, parameterize, batch, update, query)
ResultSetHandler (handleResultSets, handleOutputParameters)

调用拦截器

CallableStatementHandler #query

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 
   
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    List<E> resultList = resultSetHandler.handleResultSets(cs);
    resultSetHandler.handleOutputParameters(cs);
    return resultList;
  }

调用拦截器

List resultList = resultSetHandler.handleResultSets(cs);

调用拦截器

resultSetHandler.handleOutputParameters(cs);

  public void handleOutputParameters(CallableStatement cs) throws SQLException { 
   
    final Object parameterObject = parameterHandler.getParameterObject();
    //省略......
  }

调用拦截器

ParameterHandler # getParameterObject

另外两种
PreparedStatementHandler
SimpleStatementHandler
只有这一行调用了一次拦截器
List resultList = resultSetHandler.handleResultSets(cs);

而RoutingStatementHandler 是从新路由到上面三种的其中一种。

这样一整个doQuery 方法的拦截器调用完成

图1:SqlSessionFactory 到 doQuery
在这里插入图片描述
图2:doQuery – close
在这里插入图片描述

所以一个正常的查询被拦截器拦截的顺序应为:
Executor -> query
StatementHandler -> prepare
StatementHandler -> parameterize
ParameterHandler -> setParameters
StatementHandler -> query
ResultSetHandler -> handleResultSets
ResultSetHandler -> handleOutputParameters
ParameterHandler -> getParameterObject

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/197346.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Linux终端工具_ubuntu终端命令大全

    Linux终端工具_ubuntu终端命令大全本文中,介绍了14款最佳Linux命令行终端工具,可以用来替代debian系的Linux原生终端。如果你每天需要花大量的时间使用Linux命令行,而且正在寻找一些可替代系统自带的老旧且乏味的终端软件,不妨看看这篇文章,或许能给你带来一些帮助。如果你跟我一样,整天要花大量的时间使用Linux命令行,而且正在寻找一些可替代系统自带的老旧且乏味的终端软件,那你真是找对了文章。我这里搜集了一些非常有趣的终端…

    2022年8月21日
    10
  • jvmxmx和xms参数分析(设定优化校准)

    XmnXmsXmxXss有什么区别Xmn、Xms、Xmx、Xss都是JVM对内存的配置参数,我们可以根据不同需要区修改这些参数,以达到运行程序的最好效果。-Xms堆内存的最小大小,默认为物理内存的1/64-Xmx堆内存的最大大小,默认为物理内存的1/4-Xmn堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn-Xss设置每个线程可使用的内存大小,即…

    2022年4月18日
    46
  • 使用postcss-plugin-px2rem和postcss-pxtorem(postcss-px2rem)-px自动转换rem的配置方法-vue-cli3.0

    使用postcss-plugin-px2rem和postcss-pxtorem(postcss-px2rem)-px自动转换rem的配置方法-vue-cli3.0在vue-cli3.0中使用postcss-plugin-px2rem插件插件的作用是自动将vue项目中的px转换为rempostcss-plugin-px2rem优势:  因为postcss-plugin-px2rem这个插件配置选项上有exclude属性,它可以配置是否对某个文件夹下的所有css文件不进行从px到rem的转换。  所以我们可以利用这个特性,把项目中的node_module文件夹排除掉。这样如果我们项目中是用了,前端UI框架的话,就不会吧UI框架(…

    2025年6月10日
    3
  • 为知笔记怎么导入文件「建议收藏」

    为知笔记怎么导入文件「建议收藏」方法1:拖动文件到左侧的笔记文件夹方法2、鼠标右键文件,选择发送到—》为知笔记

    2022年8月31日
    3
  • ps切图怎么做成html,PS切图怎么导出网页 PS切图怎么生成源代码

    ps切图怎么做成html,PS切图怎么导出网页 PS切图怎么生成源代码PS切片工具切出来的切图可怎么导出网页?PS切图怎么生成源代码?PS切片的网址和源代码功能在PS切片的编辑功能里,添加URL地址,切片存储为WEB所有格式,优化存储结果保存成“HTML和图像”或者“仅HTML”。这样保存出来的切片就是网页的图片,带有源代码功能。下面来看看PS切图导出网页和生成源代码的图文教程。PS切片怎么添加网址1、直接找一张图片来,用PS打开,将需要添加的键的图像切出来,选择切…

    2025年6月29日
    2
  • idea2022最新激活码 csdn【中文破解版】

    (idea2022最新激活码 csdn)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlLGWSVFD4PZ-eyJsaWNlbnNlSW…

    2022年4月1日
    604

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号