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

2024-03-18 04:28

本文主要是介绍MyBatis3源码深度解析(十三)MyBatis的核心组件(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 前言
    • 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.Configurationprotected 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.ExecutorTypepublic 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对象,类型是REUSEExecutor 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.MappedStatementpublic final class MappedStatement {private String id;private Integer fetchSize;private Integer timeout;private StatementType statementType;private ResultSetType resultSetType;private ParameterMap parameterMap;// resultMapprivate List<ResultMap> resultMaps;// flushCacheprivate boolean flushCacheRequired;private boolean useCache;private boolean resultOrdered;// useGeneratedKeysprivate KeyGenerator keyGenerator;// keyPropertyprivate String[] keyProperties;// keyColumnprivate String[] keyColumns;private String databaseId;private LanguageDriver lang;private String[] resultSets;// ......
}

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

源码6org.apache.ibatis.mapping.MappedStatementpublic 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.StatementHandlerpublic 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.TypeHandlerpublic 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.StringTypeHandlerpublic class StringTypeHandler extends BaseTypeHandler<String> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)throws SQLException {ps.setString(i, parameter);}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {return rs.getString(columnName);}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return rs.getString(columnIndex);}@Overridepublic 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.TypeHandlerRegistrypublic 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.ParameterHandlerpublic 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);}// 获取参数对应的TypeHandlerTypeHandler 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.ResultSetHandlerpublic 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实例创建过程。

这篇关于MyBatis3源码深度解析(十三)MyBatis的核心组件(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821169

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、