MyBatis3源码深度解析(十三)MyBatis的核心组件(二)

news2025/2/27 8:28:21

文章目录

    • 前言
    • 4.3 Configuration组件
      • 4.3.9 mappedStatements
      • 4.3.10 Configuration组件的其它属性
    • 4.4 Executor
    • 4.5 MappedStatement
    • 4.6 StatementHandler
    • 4.7 TypeHandler
    • 4.8 ParameterHandler
    • 4.9 ResultSetHandler
    • 4.10 小结

前言

MyBatis框架的配置信息有两种,一种是配置MyBatis框架属性的主配置文件,另一种是配置可执行的SQL语句的Mapper配置文件。

Configuration组件的作用除了描述主配置文件mybatis-config.xml的信息,还描述Mapper配置文件的信息。

4.3 Configuration组件

4.3.9 mappedStatements

源码1org.apache.ibatis.session.Configuration

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
      "Mapped Statements collection")
          .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and "
              + targetValue.getResource());

由 源码1 可知,Configuration组件组合了一个Map集合用于存放MappedStatement对象。

MappedStatement对象用于描述<insert><update><delete><select>等标签或通过@Insert、@Update、@Delete、@Select等注解配置的SQL信息。

MyBatis将所有的MappedStatement对象注册到mappedStatements属性中,其中Key为Mapper的ID,Value为MappedStatement对象。

在【MyBatis3源码深度解析(十二)MyBatis的核心组件(一)Configuration 】中编写的示例项目中,UserMapper接口和UserMapper.xml文件如下所示,mappedStatements属性就是用于描述这其中的SQL语句的。

public interface UserMapper {

    List<User> selectAll();
    
    @Select("select * from user where id = #{id, jdbcType=INTEGER}")
    User selectById(@Param("id") Integer id);
}
<mapper namespace="com.star.mybatis.mapper.UserMapper">
    <select id="selectAll" resultType="User">
        select * from user
    </select>
</mapper>

借助Debug工具,可以发现上述两条SQL语句都注册到了mappedStatements属性中:

4.3.10 Configuration组件的其它属性

除了mappedStatements属性,Configuration组件还有一些其它属性用于描述SQL映射文件:

源码2org.apache.ibatis.session.Configuration

// 保存<cache>标签的信息
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
// 保存<resultMap>标签的信息
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
// 保存<parameterMap>标签的信息
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
// 保存<selectKey>标签的信息
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
// 用于注册所有Mapper XML配置文件路径
protected final Set<String> loadedResources = new HashSet<>();
// 保存<sql>标签的信息
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

除此之外,Configuration组件还作为Executor、StatementHandler、ResultSetHandler、ParameterHandler组件的工厂类,用于创建这些组件的实例。

源码3org.apache.ibatis.session.Configuration

// ParameterHandler组件工厂方法
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
        BoundSql boundSql) {...}
        
// ResultSetHandler组件工厂方法
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,
        ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {...}
        
// StatementHandler组件工厂方法
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
        Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {...}
        
// Executor组件工厂方法
public Executor newExecutor(Transaction transaction) {...}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {...}

这些工厂方法会根据MyBatis不同的配置创建对应的实现类。例如,Executor组件有4种不的实现,分别为BatchExecutor、ReuseExecutor、SimpleExecutor、CachingExecutor,具体返回哪种则根据ExecutorType对象的值来决定。

源码4org.apache.ibatis.session.ExecutorType

public enum ExecutorType {
    SIMPLE,
    REUSE,
    BATCH
}

当ExecutorType的参数值为REUSE时,newExecutor()方法返回的是ReuseExecutor实例,当数值为SIMPLE时,返回的是SimpleExecutor实例,这是典型的工厂方法模式的应用。

4.4 Executor

SqlSession是MyBatis提供的操作数据库的API,但是真正执行SQL的是Executor组件。

Executor接口中定义了对数据库的增删改查方法,其中query()queryCursor()方法用于执行查询作,update()方法用于执行插入、删除、修改操作。

借助IDE,可以得到Executor接口的继承关系图:

由上图可知,MyBatis提供了4种不同的Executor,分别是BatchExecutor、ReuseExecutor、SimpleExecutor、CachingExecutor。

其中,前面三种类型的Executor都继承至BaseExecutor,而BaseExecutor中定义了方法的通用执行流程及处理逻辑,具体由子类实现。

SimpleExecutor是基础的Executor,能完成基本的增删改查操作。

ReuseExecutor对JDBC中的Statement对象做了缓存,当执行相同的SQL语句时,可以直接从缓存中取出Statement对象进行复用,避免频繁创建和销毁Statement对象,从而提升系统性能。

BatchExecutor实现了批量处理多条SQL语句的功能。

另外,当MyBatis开启了二级缓存功能时,会使用CachingExecutor对SimpleExecutor、ResueExecutor、BatchExecutor进行装饰,为查询操作增加二级缓存功能。

下面是一个Executor组件的使用示例:

@Test
public void testExecutor() throws IOException, SQLException {
    Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 从会话中获取Configuration对象
    Configuration configuration = sqlSession.getConfiguration();
    // 从Configuration对象中获取MappedStatement对象
    MappedStatement mappedStatement = configuration.getMappedStatement("com.star.mybatis.mapper.UserMapper.selectAll");
    // 创建Executor对象,类型是REUSE
    Executor executor = configuration.newExecutor(new JdbcTransaction(sqlSession.getConnection()),
            ExecutorType.REUSE);
    // 执行具体的SQL语句
    List<User> userList = executor.query(mappedStatement, null, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
    userList.forEach(System.out::println);
}

执行单元测试,控制台打印执行结果:

User{id=1, name='孙悟空', age=1500, phone='18705464523', birthday=Thu Jan 01 00:00:00 CST 1}
User{id=2, name='猪八戒', age=1000, phone='15235468789', birthday=Fri Mar 10 00:00:00 CST 500}

MyBatis在启动时,会将Mapper配置文件解析成MappedStatement对象注册到Configuration组件中,因此在示例中可以直接从Configuration对象中获取MappedStatement对象。

同时,Configuration组件还是Executor组件的工厂对象,因此可以直接创建一个Executor对象,然后以MappedStatement对象为参数执行其query()方法真正执行SQL语句。

4.5 MappedStatement

MyBatis通过MappedStatement组件来描述<select><update><insert><delete>等标签或者@Select@Update@Insert@Delete等注解所配置的SQL信息。

不同类型的SQL语句需要使用对应的XML标签或注解来进行配置。这些标签或注解提供了很多属性用来控制每条SQL语句的执行行为。

借助IDE,可以列出标签中包含的属性:

这些属性的含义是:

表格来源:W3CSchool MyBatis教程 MyBatis XML映射文件。

借助IDE,可以列出标签和标签的所有属性:

可见,标签和标签的属性是一样的,它们的大部分属性与标签一致,但其中有3个属性是特有的,仅对和标签有效:

  • useGeneratedKeys:当该属性为true时,MyBatis可以使用JDBC的getGeneratedKeys方法取出由数据库内部生成的主键,如MySQL中的自增主键。默认为false。
  • keyColumn:用于设置表中的主键的列名,当主键列不是表中的第一列时需要设置。如果有多个字段,则使用逗号分隔。
  • keyProperty:用于设置Java对象的属性,数据库的自增主键或者标签中的标签返回的值将映射到该属性中。如果有多个属性,则使用逗号分隔。

借助IDE,可以列出标签的所有属性:

可见,标签的所有属性都是标签是一样的,没有特有的属性。

对应到MappedStatement组件,有一一对应的属性来保存标签中的属性信息:

源码5org.apache.ibatis.mapping.MappedStatement

public final class MappedStatement {

    private String id;
    private Integer fetchSize;
    private Integer timeout;
    private StatementType statementType;
    private ResultSetType resultSetType;
    private ParameterMap parameterMap;
    // resultMap
    private List<ResultMap> resultMaps;
    // flushCache
    private boolean flushCacheRequired;
    private boolean useCache;
    private boolean resultOrdered;
    // useGeneratedKeys
    private KeyGenerator keyGenerator;
    // keyProperty
    private String[] keyProperties;
    // keyColumn
    private String[] keyColumns;
    private String databaseId;
    private LanguageDriver lang;
    private String[] resultSets;
    // ......
}

此外,MappedStatement组件还有一些其他的属性,它们的含义如下:

源码6org.apache.ibatis.mapping.MappedStatement

public final class MappedStatement {
    
    // 解析<select|insert|update|delete>标签,将SQL语句配置信息解析为SqlSource对象
    private SqlSource sqlSource;
    // 二级缓存实例
    private Cache cache;
    // Mapper配置文件的路径
    private String resource;
    // Configuration对象的引用,方便获取MyBatis的配置
    private Configuration configuration;
    // 主键生成策略,默认为Jdbc3KeyGenerator,即数据库自增主键
    private KeyGenerator keyGenerator;
    // <ResultMap>标签中是否有嵌套的<ResultMap>
    private boolean hasNestedResultMaps;
    // 用于输出日志
    private Log statementLog;
    // ......
}

4.6 StatementHandler

StatementHandler组件封装了对JDBC的Statement的操作,可以用于设置Statement对象的属性、调用Statement对象与数据库进行交互等。

StatementHandler接口中的定义如下:

源码7org.apache.ibatis.executor.statement.StatementHandler

public interface StatementHandler {
    // 创建Statement对象并完成属性设置
    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
    // 使用ParameterHandler组件为PreparedStatement和CallableStatement的参数占位符设置值
    void parameterize(Statement statement) throws SQLException;
    // 将SQL语句添加到批量处理执行列表中
    void batch(Statement statement) throws SQLException;
    // 
    int update(Statement statement) throws SQLException;
    // 执行查询语句,并使用ResultHandler处理查询结果集
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
    // 执行带游标的查询语句,适用于查询数据量较大的情况
    <E> Cursor<E> queryCursor(Statement statement) throws SQLException;
    // 获取Mapper中配置的SQL信息,包括SQL文本和参数映射信息等
    BoundSql getBoundSql();
    // 获取ParameterHandler实例
    ParameterHandler getParameterHandler();

}

由 源码7 可知,StatementHandler接口定义的方法都是模板方法,具体由其子类实现。

借助IDE,可以得到StatementHandler接口的继承关系图:

在继承关系图中,BaseStatementHandler是一个抽象类,封装了通用的处理逻辑及方法执行流程,具体方法的实现由子类完成。

SimpleStatementHandler封装了对JDBC的Statement对象的操作,PreparedStatementHandler封装了对JDBC的PreparedStatement对象的操作,CallableStatementHandler封装了对JDBC的CallableStatement对象的操作。

RoutingStatementHandler则是一个创建StatementHandler实例的组件,会根据Mapper配置中的statementType属性(取值为STATEMENT、PREPARED、CALLABLE)创建对应类型的StatementHandler实例。

4.7 TypeHandler

涉及Java类型和JDBC类型转换的情况有两种:

(1)PreparedStatement对象为参数占位符设置值时,需要调用PreparedStatement接口的setXXX()方法,将Java类型转换为对应的JDBC类型并为参数占位符赋值。
(2)执行SQL语句获取ResultSet对象后,需要调用ResultSet对象的getXXX()方法,将JDBC类型转换为Java类型。

MyBatis中使用TypeHandler组件来完成Java类型和JDBC类型的互换。

TypeHandler接口的定义如下:

源码8org.apache.ibatis.type.TypeHandler

public interface TypeHandler<T> {
    // 为PreparedStatement对象的参数占位符设置值
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    // 根据列名获取该列的值
    T getResult(ResultSet rs, String columnName) throws SQLException;
    // 根据列索引获取该列的值
    T getResult(ResultSet rs, int columnIndex) throws SQLException;
    // 获取存储过程的调用结果
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

在【MyBatis3源码深度解析(十二)MyBatis的核心组件(一)Configuration 4.3.3 类型处理器】中就提到,MyBatis内置了非常多的TypeHandler实现类,而这些实现类都继承了一个抽象类BaseTypeHandler。

BaseTypeHandler类实现了TypeHandler接口,定义了一些通用的处理逻辑。对setParameter()方法中参数为null时的情况做了处理,对getResult()方法可能出现的异常做了处理。

因此,如果需要自定义一个TypeHandler,只需要继承BaseTypeHandler类即可。

举个例子,StringTypeHandler用于处理java.lang.String类型和JDBC的CHAR、VARCHAR、LONGVARCHAR、NCHAR、NVARCHAR、LONGNVARCHAR等类型的转换。其源码如下:

源码9org.apache.ibatis.type.StringTypeHandler

public class StringTypeHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
            throws SQLException {
        ps.setString(i, parameter);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

由 源码9 可知,StringTypeHandler在设置值调用的是PreparedStatement对象的setString()方法,获取值时调用的是PreparedStatement对象的getString()方法。其他类型的TypeHandler的处理逻辑与之类似。

MyBatis通过TypeHandlerRegistry来建立JDBC类型、Java类型与TypeHandler之间的映射关系。

由类名可知,这是一个TypeHandler的注册器,其源码如下:

源码10org.apache.ibatis.type.TypeHandlerRegistry

public final class TypeHandlerRegistry {
    // 保存JDBC类型与TypeHandler的映射关系
    private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
    // 保存Java类型与TypeHandler的映射关系
    private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
    // 保存Class对象与TypeHandler的映射关系
    private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
    // ......
    
    public TypeHandlerRegistry(Configuration configuration) {
        // .....
        register(Boolean.class, new BooleanTypeHandler());
        register(boolean.class, new BooleanTypeHandler());
        register(JdbcType.BOOLEAN, new BooleanTypeHandler());
        register(JdbcType.BIT, new BooleanTypeHandler());
        // .....
    }
}

由 源码10 可知,TypeHandlerRegistry中组合了几个Map集合,用于保存JDBC类型、Java类型、Class对象等与TypeHandler之间的映射关系。

在其构造方法中,使用register()方法注册所有内置的TypeHandler。对于自定义的TypeHandler,也可以调用TypeHandlerRegistry的register()方法进行注册。

4.8 ParameterHandler

前面提到,当使用PreparedStatement或者CallableStatement时,如果SQL语句中有参数占位符,则需要ParameterHandler组件为参数占位符赋值。

ParameterHandler接口的定义如下:

源码11org.apache.ibatis.executor.parameter.ParameterHandler

public interface ParameterHandler {
    // 获取执行Mapper时传入的参数对象
    Object getParameterObject();
    // 为参数占位符设置值
    void setParameters(PreparedStatement ps) throws SQLException;
}

ParameterHandler接口只有一个默认的实现类,即DefaultParameterHandler。

源码12org.apache.ibatis.scripting.defaults.DefaultParameterHandler

@Override
public void setParameters(PreparedStatement ps) {
    // ......
    // 获取所有参数的映射信息
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        MetaObject metaObject = null;
        // 遍历所有参数的映射信息
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                // 获取参数名
                String propertyName = parameterMapping.getProperty();
                // 根据参数名获取传入的参数值
                if (boundSql.hasAdditionalParameter(propertyName)) {
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    if (metaObject == null) {
                        metaObject = configuration.newMetaObject(parameterObject);
                    }
                    value = metaObject.getValue(propertyName);
                }
                // 获取参数对应的TypeHandler
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                // 获取JDBC类型
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    // 调用TypeHandler的setParameter方法设置参数值
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } // catch ...
            }
        }
    }
}

由 源码12 可知,MyBatis通过ParameterMapping描述参数映射的信息,DefaultParameterHandler类的setParameters()首先获取所有的参数映射信息并进行遍历,然后根据参数名称获取对应的参数值,通过调用TypeHandler的setParameter()方法为Statement对象中的参数占位符设置参数值。

4.9 ResultSetHandler

ResultSetHandler用于StatementHandler对象执行完查询操作或存储过程后,对结果集或者存储过程的执行结果进行处理。

ResultSetHandler接口的定义如下:

源码13org.apache.ibatis.executor.resultset.ResultSetHandler

public interface ResultSetHandler {
    // 对ResultSet对象进行处理并返回包含结果实体的List对象
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    // 将ResultSet对象包装成Cursor对象返回,对Cursor进行遍历时
    // 能够动态地从数据库查询数据,避免一次性将所有数据加载到内存中
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
    // 处理存储过程调用结果
    void handleOutputParameters(CallableStatement cs) throws SQLException;
}

ResultSetHandler接口只有一个默认实现,即DefaultResultSetHandler。

源码14org.apache.ibatis.executor.resultset.DefaultResultSetHandler

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    // ......
    final List<Object> multipleResults = new ArrayList<>();
    int resultSetCount = 0;
    // 1.获取ResultSet对象,并将其包装为ResultSetWrapper对象
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 2.获取ResultMap信息
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 3.真正处理结果集
        handleResultSet(rsw, resultMap, multipleResults, null);
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
    }
    
    // ......
}

由 源码14 可知,DefaultResultSetHandler的handleResultSets()方法的逻辑如下:

(1)从Statement对象中获取ResultSet对象,并包装成ResultSetWrapper对象,通过ResultSetWrapper对象可以更方便地获取表字段名称、字段对应的TypeHandler信息等。
(2)获取解析Mapper接口及SQL配置的ResultMap信息,一条语句一般对应一个ResultMap。
(3)调用handleResultSet()方法对ResultSetWrapper对象进行处理,将生成的实体对象存放在multipleResults列表中并返回。

4.10 小结

第四章到此就梳理完毕了,本章的主题是:MyBatis的核心组件。回顾一下本章的梳理的内容:

(十二)Configuration组件
(十三)Executor、MappedStatement、StatementHandler、TypeHandler、ParameterHandler、ResultSetHandler组件

这些工具类在MyBatis源码中出现的频率较高,了解这些工具类的使用及实现原理有助于深入研究MyBatis的源码。

更多内容请查阅分类专栏:MyBatis3源码深度解析

第五章主要学习:SqlSession的创建过程。主要内容包括:

  • XPath方式解析XML文件;
  • Configuration实例创建过程;
  • SqlSession实例创建过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1524061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter开发进阶之使用工具效率开发

Flutter开发进阶之使用工具效率开发 软件开发团队使用Flutter开发的原因通常是因为Flutter开发性能高、效率高、兼容性好、可拓展性高&#xff0c;作为软件PM来说主要考虑的是范围管理、进度管理、成本管理、资源管理、质量管理、风险管理和沟通管理等&#xff0c;可以看到Flu…

OpenCV图像相似性及相似度比对算法

背景 在做图像处理或者计算机视觉相关的项目的时候&#xff0c;很多时候需要我们对当前获得的图像和上一次的图像做相似性比对&#xff0c;从而找出当前图像针对上一次的图像的差异性和变化点&#xff0c;这需要用到OpenCV中的一些图像相似性和差异性的比对算法&#xff0c;在O…

Java零基础入门-如何代码模拟斗地主洗牌发牌动作(上)?

一、本期教学目标 掌握map集合常用方法。掌握map集合使用场景。通过map集合实现斗地主洗牌发牌动作。 二、前言 对于双列集合之map集合&#xff0c;它的相关知识点及拓展咱们都已经差不多学完了。接下来&#xff0c;就是带着大家如何灵活运用map进行一个实战教学&#xff0c;…

【方法封装】时间格式化输出,获取请求设备和IP

目录 时间类 1.1 获取当前时间&#xff0c;以特定格式化形式输出 1.2 自定义时间&#xff0c;以特定格式化输出 1.3 获取当前时间&#xff0c;自定义格式化 1.4 自定义时间&#xff0c;自定义格式化 设备类 根据请求头信息&#xff0c;获取用户发起请求的设备 请求IP类 …

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:TabContent)

仅在Tabs中使用&#xff0c;对应一个切换页签的内容视图。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 支持单个子组件。 说明&#xff1a; 可内置系统组件和自定义组件&#xff0c;支…

【SpringCloud微服务实战08】RabbitMQ 消息队列

MQ异步通信优缺点: 优点: 吞吐量提升:无需等待订阅者处理完成,响应更快速 故障隔离:服务没有直接调用,不存在级联失败问题 调用间没有阻塞,不会造成无效的资源占用 耦合度极低,每个服务都可以灵活插拔,可替换 流量削峰:不管发布事件的流量波动多大,都由Broker接收,…

终止代码: DRIVER IRQL NOT LESS OR EQUAL 失败的操作:Netwtw12.sys

蓝屏警告&#xff1a; 今天电脑浏览器用着用着就蓝屏重启&#xff0c;蓝屏上报这个错误&#xff1a; 上网找了一堆&#xff0c;发现关键是这句话&#xff1a;“失败的操作:Netwtw12.sys” 最终在一顿操作下&#xff0c;发现了是23年更新的网卡&#xff08;Intel(R) Wi-Fi6E A…

搜维尔科技:工作室选择 OptiTrack 进行新的虚拟制作舞台

35North Studios 成立于 2020 年&#xff0c;是一家最先进的制作工作室。他们的全方位服务方法可帮助电影制片人和企业在一个设备齐全且先进的地点规划、拍摄、编辑、评分和完成项目。该工作室位于爱荷华州克利尔湖&#xff0c;为创作者提供了一个安静的空间&#xff0c;让他们…

算法思想总结:滑动窗口算法

创作不易&#xff0c;感谢三连 一.长度最小的数组 . - 力扣&#xff08;LeetCode&#xff09;长度最小的数组 class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {int lenINT_MAX,nnums.size(),sum0;//len必须要给一个很大的数&#xf…

【Linux】信号保存{sigset_t/sigpending/sigprocmask/bash脚本/代码演示}

文章目录 1.信号相关常见概念2.管理信号的数据结构3.初识sigset_t4.信号集操作函数4.1sigpending4.2sigprocmask4.2代码测试1.测试12.测试23.测试3 4.3bash 脚本文件 1.信号相关常见概念 信号相关动作&#xff1a;产生 发送 接收 阻塞 递达(处理) 实际执行信号的处理动作称为信…

vue2+vant2+Laravel7 实现多图上传到七牛云

后端接口 1、路由&#xff0c;在 routes/api.php 中 Route::resource(photos, PhotoController)->only(store);2、创建对应控制器 <?php namespace App\Http\Controllers; use Illuminate\Http\Request;class PhotoController extends Controller {/**** 上传图片* p…

面部表情参考图

创造表情形变 | Character Creator | Reallusion 皮笑肉不笑&#xff1f;读取情绪的AI说&#xff1a;我太难了_面部

HAProxy高性能负载均衡器

一、HAProxy基础知识 &#xff08;一&#xff09;HAProxy概述 HAProxy是一款基于事件驱动、单进程模型设计的四层与七层负载均衡器&#xff0c;它能够在TCP/UDP层面以及HTTP(S)等应用层协议上实现高效的流量分发。HAProxy不仅适用于Web服务器负载均衡&#xff0c;还能应用于数据…

RabbitMQ学习总结-基础篇

1..RabbitMQ 本身是一个消息中间件&#xff0c;在服务应用中&#xff0c;可解决高性能&#xff0c;高并发&#xff0c;高应用的问题&#xff0c;极大程度上解决了应用的性能问题。 2.MQ的使用分为生产者和消费者&#xff0c;生产者生产消息&#xff0c;消费者去消费消息。 3.…

cesium.js加载模型后,重新设置旋转角度属性值

// 加载模型var position Cesium.Cartesian3.fromDegrees(longitude, latitude, height);// 计算矩阵var rollAngleDegrees 15; // 设置翻滚角度var rollAngleRadians Cesium.Math.toRadians(rollAngleDegrees); // 将角度转换为弧度var orientation Cesium.Transforms.eas…

android 怎么自定义view

首先了解view的绘制流程&#xff1a; 所以onmeasure ---测量view onlayout---确定view大小----》所以继承ViewGroup必须要重写onlayout&#xff0c;确定子view 而onDraw----是继承view时候需要操作的。 所以&#xff1a;自定义ViewGroup一般是利用现有的组件根据特定的布局…

Python电梯楼层数字识别

程序示例精选 Python电梯楼层数字识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《Python电梯楼层数字识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应…

操作系统内功篇:硬件结构之如何写出让CPU执行更快的代码?

一 前言 因为CPU要操作的数据都在CPU Cache中的话&#xff0c;就不用再从内存中读取数据了&#xff0c;这样就提高了效率&#xff0c;访问的数据在CPU Cache中越多&#xff0c;有个专业名词称为缓存命中率高&#xff0c;所以说&#xff0c;缓存命中率越高&#xff0c;自然执行…

Jupyter Notebook出错提示An error occurred while retrieving package information解决办法

出错日志信息&#xff1a; To access the notebook, open this file in a browser:file:///C:/Users/colda/AppData/Roaming/jupyter/runtime/nbserver-14564-open.htmlOr copy and paste one of these URLs:http://localhost:8888/?token3c0113e5da07c0b8b8c9de74ffb453c5047…

在idea中配置tomcat服务器,然后部署一个项日

1.下载tomcat Tomcat下载 点击右边的tomcat8 找到zip点击下载 下载完&#xff0c;解压到你想放置的路径下 2.配置环境变量 打开设置找到高级系统设置点击环境变量 点击新建&#xff0c;变量名输入&#xff1a;CATALINA_HOME&#xff0c;变量值就是Tomcat的安装路径&#x…