广告
返回顶部
首页 > 资讯 > 精选 >Mybatis SqlSession执行流程介绍
  • 921
分享到

Mybatis SqlSession执行流程介绍

2023-06-20 15:06:53 921人浏览 薄情痞子
摘要

这篇文章主要讲解了“mybatis sqlSession执行流程介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Mybatis SqlSession执行流程介绍”吧!目录Mybatis执行

这篇文章主要讲解了“mybatis sqlSession执行流程介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Mybatis SqlSession执行流程介绍”吧!

目录
  • Mybatis执行SQL流程

    • SqlSession

    • Executor

  • Mybatis之Executor

    • Mybatis之StatementHandler

      • 进入ResultSetHandler

    Mybatis执行SQL流程

    在看源码之前,我们需要了解一些基本知识,如果您没有阅读Mybatis SqlSessionFactory 初始化原理,可以先阅读Mybatis SqlSessionFactory 初始化原理这篇文章,这用更有助于我们理解接下来的文章
    在看源码之前,我们需要了解一些基本知识

    SqlSession

    SqlSession是一个接口,它有两个实现类:
     - DefaultSqlSession:默认实现类
     - SqlSessionManager:已经弃用的实现类,所以我们不需要关注他
    SqlSession是与数据库交互的顶层类,通常与ThreadLocal绑定,一个会话使用一个SqlSession,SqlSession是线程安全的,使用完毕需要close()

    public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor;}

    SqlSession中最重要的两个变量:
     - Configuration:核心配置类,也是初始化时传过来的
     - Executor:实际执行SQL的执行器

    Executor

    Executor是一个接口,有三个实现类
     - BatchExecutor 重用语句,并执行批量更新
     - ReuseExecutor 重用预处理语句prepared statements
     - SimpleExecutor 普通的执行器,默认使用

    了解完基本知识后,我们接着往下看代码。

    当创建完SqlSessionFactory后,就可以创建SqlSession,然后使用SqlSession进行增删改查:

    // 1. 读取配置文件,读成字节输入流,注意:现在还没解析InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");// 2. 解析配置文件,封装Configuration对象   创建DefaultSqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();List<Object> objects = sqlSession.selectList("namespace.id");

    我们先去看openSession()方法,创建了SqlSession

    //6. 进入openSession方法@Overridepublic SqlSession openSession() {    //getDefaultExecutorType()传递的是SimpleExecutor    // level:数据库事物级别,null    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}//7. 进入openSessionFromDataSource。//ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务//openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {        // 获得 Environment 对象        final Environment environment = configuration.getEnvironment();        // 创建 Transaction 对象        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);        // 创建 Executor 对象        final Executor executor = configuration.newExecutor(tx, execType);        // 创建 DefaultSqlSession 对象        return new DefaultSqlSession(configuration, executor, autoCommit);    } catch (Exception e) {        // 如果发生异常,则关闭 Transaction 对象        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,Transaction,Executor,DefaultSqlSession对象,并且对于会话对象来说,他的autoCommit默认为false,默认不自动提交。

    然后我回到原来的代码,接着就需要使用SqlSession进行增删改查操作了

    所以我们进入selectList()查看

    //8.进入selectList方法,多个重载方法@Overridepublic <E> List<E> selectList(String statement) {    return this.selectList(statement, null);}@Overridepublic <E> List<E> selectList(String statement, Object parameter) {    return this.selectList(statement, parameter, RowBounds.DEFAULT);}@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {        // 获得 MappedStatement 对象        MappedStatement ms = configuration.getMappedStatement(statement);        // 执行查询        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);    } catch (Exception e) {        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {        ErrorContext.instance().reset();    }}

    selectList有多个重载方法,进入到最终方法后,我们可以看到它做了两件事

    • 通过statementId,从Configuration中取MappedStatement对象,就是存放了sql语句,返回值类型,输入值类型的对象

    • 然后委派Executor执行器去执行具体的增删改查方法

    所以,对于实际JDBC的操作,我们还需要进入Executor中查看

    Mybatis之Executor

    我们继续从刚刚selectList源码中,进入Executor查看

    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    //此方法在SimpleExecutor的父类BaseExecutor中实现@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    //根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示    BoundSql boundSql = ms.getBoundSql(parameter);    //为本次查询创建缓存的Key    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);    // 查询    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}

    拆分成了三大步:

    (1)先调用MappedStatement的getBoundSql方法,获取解析后的SQL语句,解析工作是由SqlSourceBuilder完成的

    什么叫解析后的SQL语句呢?因为Mybatis编写SQL语句时,会用到动态SQL,比如#{}占位符,这种占位符JDBC是不认识的,所以需要将其转换成?占位符,并且将其内部的字段名存储起来,后面填充参数的时候好使用反射获取值。

    public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {    // 创建 ParameterMappingTokenHandler 对象    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);    // 创建 GenericTokenParser 对象    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);    // 执行解析    String sql = parser.parse(originalSql);    // 创建 StaticSqlSource 对象    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());}

    上面代码就可以看到,会将拆分#{和},进行解析

    (2)根据查询条件,创建缓存key,用来接下来去缓存查找是否有已经执行过的结果

    (3)调用重载query()方法

    接着我们进入重载方法查看:

    @Override    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());        // 已经关闭,则抛出 ExecutorException 异常        if (closed) {            throw new ExecutorException("Executor was closed.");        }        // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。        if (queryStack == 0 && ms.isFlushCacheRequired()) {            clearLocalCache();        }        List<E> list;        try {            // queryStack + 1            queryStack++;            // 从一级缓存中,获取查询结果            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;            // 获取到,则进行处理            if (list != null) {                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);            // 获得不到,则从数据库中查询            } else {                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);            }        } finally {            // queryStack - 1            queryStack--;        }        if (queryStack == 0) {            // 执行延迟加载            for (DeferredLoad deferredLoad : deferredLoads) {                deferredLoad.load();            }            // issue #601            // 清空 deferredLoads            deferredLoads.clear();            // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {                // issue #482                clearLocalCache();            }        }        return list;    }

    主要的逻辑:

    • 从一级缓存取数据,如果有直接使用缓存的进行接下来的操作

    • 如果没有,从数据库查询

    进入queryFromDatabase()方法:

    // 从数据库中读取操作private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    List<E> list;    // 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 `DeferredLoad#canLoad()` 方法    localCache.putObject(key, EXECUTION_PLACEHOLDER);    try {        // 执行读操作        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);    } finally {        // 从缓存中,移除占位对象        localCache.removeObject(key);    }    // 添加到缓存中    localCache.putObject(key, list);    // 暂时忽略,存储过程相关    if (ms.getStatementType() == StatementType.CALLABLE) {        localOutputParameterCache.putObject(key, parameter);    }    return list;}@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {        Configuration configuration = ms.getConfiguration();        // 传入参数创建StatementHanlder对象来执行查询        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);        // 创建jdbc中的statement对象        stmt = prepareStatement(handler, ms.getStatementLog());        // 执行 StatementHandler  ,进行读操作        return handler.query(stmt, resultHandler);    } finally {        // 关闭 StatementHandler 对象        closeStatement(stmt);    }}

    通过代码可以看到,对于实际与JDBC交互的代码,Executor也懒得搞,又像SqlSession一样,委派给小弟StatementHandler了。

    Mybatis之StatementHandler

    我们从刚刚的Executor的代码查看

    @Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {        Configuration configuration = ms.getConfiguration();        // 传入参数创建StatementHanlder对象来执行查询        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);        // 创建jdbc中的statement对象        stmt = prepareStatement(handler, ms.getStatementLog());        // 执行 StatementHandler  ,进行读操作        return handler.query(stmt, resultHandler);    } finally {        // 关闭 StatementHandler 对象        closeStatement(stmt);    }}

    可以看到,这里创建完StatementHandler后,回调用prepareStatement()方法,用来创建Statement对象

    我们进入prepareStatement方法中查看

    // 初始化 StatementHandler 对象private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {    Statement stmt;    // 获得 Connection 对象    Connection connection = getConnection(statementLog);    // 创建 Statement 或 PrepareStatement 对象    stmt = handler.prepare(connection, transaction.getTimeout());    // 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符    handler.parameterize(stmt);    return stmt;}@Overridepublic void parameterize(Statement statement) throws SQLException {    //使用ParameterHandler对象来完成对Statement的设值    parameterHandler.setParameters((PreparedStatement) statement);}

    这里可以看到,它实际是使用ParameterHandler来设置Statement的参数

    @Overridepublic void setParameters(PreparedStatement ps) {    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    // 遍历 ParameterMapping 数组    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();    if (parameterMappings != null) {        for (int i = 0; i < parameterMappings.size(); i++) {            // 获得 ParameterMapping 对象            ParameterMapping parameterMapping = parameterMappings.get(i);            if (parameterMapping.getMode() != ParameterMode.OUT) {                // 获得值                Object value;                String propertyName = parameterMapping.getProperty();                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params                    value = boundSql.getAdditionalParameter(propertyName);                } else if (parameterObject == null) {                    value = null;                } else if (typeHandlerReGIStry.hasTypeHandler(parameterObject.getClass())) {                    value = parameterObject;                } else {                    MetaObject metaObject = configuration.newMetaObject(parameterObject);                    value = metaObject.getValue(propertyName);                }                // 获得 typeHandler、jdbcType 属性                TypeHandler typeHandler = parameterMapping.getTypeHandler();                JdbcType jdbcType = parameterMapping.getJdbcType();                if (value == null && jdbcType == null) {                    jdbcType = configuration.getJdbcTypeForNull();                }                // 设置 ? 占位符的参数                try {                    typeHandler.setParameter(ps, i + 1, value, jdbcType);                } catch (TypeException | SQLException e) {                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);                }            }        }    }}

    这段代码的主要目的,就是获取入参,然后根据值,来设置?占位符的参数

    TypeHandler是具体进行参数设置的对象

    所以handler.prepare(connection, transaction.getTimeout());方法,就是使用ParameterHandler来对占位符位置的参数进行值设置

    然后我们回到Executor,查看handler.query()方法

    @Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    // 执行查询    ps.execute();    // 处理返回结果    return resultSetHandler.handleResultSets(ps);}

    代码很简单,这里直接使用JDBC的PreparedStatement来进行SQL执行,然后使用ResultSetHandler进行结果数据封装处理。

    进入ResultSetHandler

    @Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());    // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List<Object> 对象。    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。    final List<Object> multipleResults = new ArrayList<>();    int resultSetCount = 0;    // 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象    ResultSetWrapper rsw = getFirstResultSet(stmt);    // 获得 ResultMap 数组    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。    List<ResultMap> resultMaps = mappedStatement.getResultMaps();    int resultMapCount = resultMaps.size();    validateResultMapsCount(rsw, resultMapCount); // 校验    while (rsw != null && resultMapCount > resultSetCount) {        // 获得 ResultMap 对象        ResultMap resultMap = resultMaps.get(resultSetCount);        // 处理 ResultSet ,将结果添加到 multipleResults 中        handleResultSet(rsw, resultMap, multipleResults, null);        // 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象        rsw = getNextResultSet(stmt);        // 清理        cleanUpAfterHandlingResultSet();        // resultSetCount ++        resultSetCount++;    }    // 因为 `mappedStatement.resultSets` 只在存储过程中使用,本系列暂时不考虑,忽略即可    // ···    // 如果是 multipleResults 单元素,则取首元素返回    return collapseSingleResultList(multipleResults);} // 处理 ResultSet ,将结果添加到 multipleResults 中private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {    try {        // 暂时忽略,因为只有存储过程的情况,调用该方法,parentMapping 为非空        if (parentMapping != null) {            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);        } else {            // 如果没有自定义的 resultHandler ,则创建默认的 DefaultResultHandler 对象            if (resultHandler == null) {                // 创建 DefaultResultHandler 对象                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);                // 处理 ResultSet 返回的每一行 Row                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);                // 添加 defaultResultHandler 的处理的结果,到 multipleResults 中                multipleResults.add(defaultResultHandler.getResultList());            } else {                // 处理 ResultSet 返回的每一行 Row                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);            }        }    } finally {        // issue #228 (close resultsets)        // 关闭 ResultSet 对象        closeResultSet(rsw.getResultSet());    }}

    代码比较多,实际最重要的代码就是

    // 添加 defaultResultHandler 的处理的结果,到 multipleResults 中multipleResults.add(defaultResultHandler.getResultList());

    将处理后的结果封装到集合中返回,这样基本Mybatis逻辑就走完了.

    我们来回顾一下,都用到了哪些类

    Mybatis SqlSession执行流程介绍

    简单总结
    SqlSessionFactoryBuilder:

    • 解析核心配置文件,创建Configuration

      • XMLConfigBuilder.parse():解析核心配置文件

      • XMLMapperBuilder.parse():解析映射配置文件MappedStatement

    • 创建SqlSessionFactory,默认创建DefaultSqlSessionFactory

    SqlSessionFactory:

    • openSession():构建Executor,SqlSession等

    SqlSession:

    • 根据statementId获取MappedStatement

    • 委派给Executor执行器执行

    Executor:

    • 使用SqlSourceBuilder,将SQL解析成JDBC认可的

    • 查询缓存,是否存在结果

    • 结果不存在,委派给StatementHandler处理器

    StatementHandler:

    • PreparedStatement:处理参数,将参数赋值到占位符上

      • TypeHandler:具体设置值的类

    • ResultSetHandler:封装结果集,封装成设置的返回值类型

      • TypeHandler:根据结果集,取出对应列

    感谢各位的阅读,以上就是“Mybatis SqlSession执行流程介绍”的内容了,经过本文的学习后,相信大家对Mybatis SqlSession执行流程介绍这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

    --结束END--

    本文标题: Mybatis SqlSession执行流程介绍

    本文链接: https://www.lsjlt.com/news/297800.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Mybatis SqlSession执行流程介绍
      这篇文章主要讲解了“Mybatis SqlSession执行流程介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Mybatis SqlSession执行流程介绍”吧!目录Mybatis执行...
      99+
      2023-06-20
    • 浅谈Mybatis SqlSession执行流程
      目录Mybatis执行SQL流程 SqlSessionExecutorMybatis之Executor Mybatis之StatementHandler 进入ResultSetHan...
      99+
      2022-11-12
    • MyBatis详细执行流程的介绍
      本篇内容介绍了“MyBatis详细执行流程的介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mybatis简介MyBatis 是一款优秀的...
      99+
      2023-06-14
    • 关于Java程序执行基本流程介绍
      下面由Java学习教程栏目给大家介绍Java程序执行基本流程,希望对需要的朋友有所帮助!让我们来看看Java程序执行流程:例如hellojava.java文件,代码如下:  public class hellojava   {   publ...
      99+
      2020-09-30
      java教程 Java
    • 关于MVC与SpringMVC的介绍、区别、执行流程
      目录介绍区别两者执行流程MVC执行流程:Spring MVC执行流程:介绍 MVC(Model-View-Controller)是一种软件架构模式,其中应用程序被划分为三个部分:模型...
      99+
      2023-05-19
      MVC SpringMVC区别 SpringMVC执行流程
    • Mybatis(五)------Mybatis执行Mapper接口的方法流程
      前面几篇文章我们介绍了JDBC、Mybatis的工具类等,下面我们开始对于mybatis的各个机制开始解析。 前面我们知道,mybatis对excutor进行封装成sqlsession提供给开发人员进行数据库的增删改查,我们先从Mybati...
      99+
      2023-09-16
      mybatis java mysql
    • Mybatis中的PageHelper的执行流程分析
      PageHelper Mybatis的执行流程 mybatis中首先要在配置文件中配置一些东西然后根据这些配置去创建一个会话工厂再根据会话工厂创建会话,会话发出操作数据库的sql语...
      99+
      2022-11-13
    • MyBatis详细执行流程的全纪录
      Mybatis简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。...
      99+
      2022-11-12
    • Mybatis执行SQL命令的流程分析
      目录MapperProxy的功能:MapperMethod的功能:Mybatis中的Sql命令,在枚举类SqlCommandType中定义的。 public enum SqlComm...
      99+
      2023-05-16
      Mybatis执行SQL命令 Mybatis执行SQL
    • MyBatis SqlSession事务与批量执行正确方式(默认不生效)
      1. 容易误用的写法 某些情况下会使用MyBatis的SqlSessionFactory.openSession()方法获取SqlSession对象,再进行数据库操作,但默认情况下SqlSession...
      99+
      2023-09-01
      mybatis 数据库 java
    • MyBatis注解CRUD与执行流程是什么
      这篇文章主要介绍了MyBatis注解CRUD与执行流程是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇MyBatis注解CRUD与执行流程是什么文章都会有所收获,下面我们一起来看看吧。结果映射ResultM...
      99+
      2023-07-05
    • Mybatis Plus 逆向工程介绍
      目录一、创建数据库二、配置pom.xml 文件三、在项目同级目录建立mgb.xml四、在测试类中写入方法一、创建数据库 二、配置pom.xml 文件    <dependen...
      99+
      2022-11-12
    • JavaScript执行机制详细介绍
      目录1.进程与线程的概念 2.浏览器原理 3.同步与异步 4.执行栈与任务队列 5.事件循环(Event-Loop) 6.定时器 前言: 不论是工作还是面试,我们可能都经常会碰到需...
      99+
      2022-11-12
    • MyBatis注解CRUD与执行流程深入探究
      目录结果映射ResultMap日志工厂STDOUT_LOGGINGLOG4J注解开发CRUDMyBatis执行流程结果映射ResultMap 引入resultMap–My...
      99+
      2023-02-21
      MyBatis注解CRUD MyBatis执行流程
    • java学习流程介绍
      首先,我个人比较推崇的学习方法是:先学java前段,也就是HTML,css,js,因为学习java以后肯定是往java ee方向发展的,学习完前端,在学习后端很多东西比计较容易理解!(推荐:java视频教程)其中J2SE是关键,如果学好了j...
      99+
      2020-08-02
      java
    • redis启动流程介绍
      1. 准备运行环境 * 设置oom handler,zmalloc分配内存失败时调用 * 初始化随机种子,用于生成随机数 * 将server参数初始化为默认值 * 创建命令与处理函数的映射表 2. 解析...
      99+
      2022-06-04
      流程 redis
    • linux启动流程介绍
      这篇文章主要介绍“linux启动流程介绍”,在日常操作中,相信很多人在linux启动流程介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”linux启动流程介绍”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
      99+
      2023-06-12
    • ASP.NETMVC过滤器执行顺序介绍
      如果某个Action过滤器运用了多种过滤器,那么过滤器的执行顺序是如何呢? 规则一:不同类型的过滤器有一个先后顺序 即执行顺序是:授权过滤器->动作过滤器->结果过滤器...
      99+
      2022-11-13
    • SpringMvc框架的简介与执行流程详解
      目录一、SpringMvc框架简介 1、Mvc设计理念2、SpringMvc简介 二、SpringMvc执行流程 1、流程图解2、步骤描述 3、核心组件 三、整合Spring框架配置...
      99+
      2022-11-12
    • 一文介绍Golang编程流程
      随着互联网技术的发展,Go语言(Golang)作为一种高效、简洁、可扩展的编程语言已经成为了工业界和开源社区中的热门选择之一。使用Go语言编程可以快速开发高效的应用程序和网络服务,并且这种编程语言经过精心设计,可以帮助开发人员避免常见的错误...
      99+
      2023-05-14
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作