EasyExcel的源码流程(导入Excel)

1. 入口

2.  EasyExcel类继承了EasyExcelFactory类,EasyExcel自动拥有EasyExcelFactory父类的所有方法,如read(),readSheet(),write(),writerSheet()等等。

3. 进入.read()方法,需要传入三个参数(文件路径,表头映射类,read监听器)
   首先调用new ExcelReaderBuilder()方法,初始化ReadWorkbook对象
   设置完readWorkbook属性后调,返回excelReaderBuilder对象 

4. 这里又个传入的参数是read监听器,进入其内部看一下,我们自定义了PersonListener实现了ReadListener。ReadListener接口源码 :

自定义的PersonListener类:

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.aliyun.odps.jdbc.utils.LoggerFactory;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;@Component
public class PersonListener extends AnalysisEventListener<PersonTest> {//一行一行读出excel内容  不读表头  EasyExcel之所以效率高,也是因它一行一行读取,解析。@Overridepublic void invoke(PersonTest personTest, AnalysisContext analysisContext) {System.out.println("***"+personTest);}//读取表头@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("表头"+headMap);}//读取完执行的方法@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("读取全部后执行");}
}

5. 接下来调用的是.sheet()方法,这里我们会传入sheetNo、sheetName参数,调用build()方法创建ExcelReader对象,传入ExcelReaderSheetBuilder构造方法中,最终创ExcelReaderSheetBuilder对象 

6. 进入build()方法,build()方法生成了ExcelReader对象,初始化ExcelAnalyser,并实例化ExcelAnalyser。

在实例化ExcelAnalyser时,choiceExcelExecutor()方法通过excel格式使用不同的执行器。
我们看XLSX中,初始化了XlsxReadContext上下文对象,给到analysisContext,又初始化了XlsxSaxAnalyser解析器对象 

public class ExcelAnalyserImpl implements ExcelAnalyser {private static final Logger LOGGER = LoggerFactory.getLogger(ExcelAnalyserImpl.class);private AnalysisContext analysisContext;private ExcelReadExecutor excelReadExecutor;/*** Prevent multiple shutdowns*/private boolean finished = false;public ExcelAnalyserImpl(ReadWorkbook readWorkbook) {try {choiceExcelExecutor(readWorkbook);} catch (RuntimeException e) {finish();throw e;} catch (Throwable e) {finish();throw new ExcelAnalysisException(e);}}private void choiceExcelExecutor(ReadWorkbook readWorkbook) throws Exception {ExcelTypeEnum excelType = ExcelTypeEnum.valueOf(readWorkbook);switch (excelType) {case XLS:POIFSFileSystem poifsFileSystem;if (readWorkbook.getFile() != null) {poifsFileSystem = new POIFSFileSystem(readWorkbook.getFile());} else {poifsFileSystem = new POIFSFileSystem(readWorkbook.getInputStream());}// So in encrypted excel, it looks like XLS but it's actually XLSXif (poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) {InputStream decryptedStream = null;try {decryptedStream = DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), readWorkbook.getPassword());XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX);analysisContext = xlsxReadContext;excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, decryptedStream);return;} finally {IOUtils.closeQuietly(decryptedStream);// as we processed the full stream already, we can close the filesystem here// otherwise file handles are leakedpoifsFileSystem.close();}}if (readWorkbook.getPassword() != null) {Biff8EncryptionKey.setCurrentUserPassword(readWorkbook.getPassword());}XlsReadContext xlsReadContext = new DefaultXlsReadContext(readWorkbook, ExcelTypeEnum.XLS);xlsReadContext.xlsReadWorkbookHolder().setPoifsFileSystem(poifsFileSystem);analysisContext = xlsReadContext;excelReadExecutor = new XlsSaxAnalyser(xlsReadContext);break;case XLSX:XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX);analysisContext = xlsxReadContext;excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, null);break;case CSV:CsvReadContext csvReadContext = new DefaultCsvReadContext(readWorkbook, ExcelTypeEnum.CSV);analysisContext = csvReadContext;excelReadExecutor = new CsvExcelReadExecutor(csvReadContext);break;default:break;}}
/*
----------------------------------------略---------------------------------------------
*/
}

7. XlsxSaxAnalyser详解

    7.1 进入new XlsxSaxAnalyser(xlsxReadContext, null)方法看一下,这里使用到SAX模式模式解析excel

public XlsxSaxAnalyser(XlsxReadContext xlsxReadContext, InputStream decryptedStream) throws Exception {this.xlsxReadContext = xlsxReadContext;// 初始化缓存(cache)XlsxReadWorkbookHolder xlsxReadWorkbookHolder = xlsxReadContext.xlsxReadWorkbookHolder();OPCPackage pkg = readOpcPackage(xlsxReadWorkbookHolder, decryptedStream);xlsxReadWorkbookHolder.setOpcPackage(pkg);// Read the Shared information StringsPackagePart sharedStringsTablePackagePart = pkg.getPart(SHARED_STRINGS_PART_NAME);if (sharedStringsTablePackagePart != null) {// 指定默认缓存defaultReadCache(xlsxReadWorkbookHolder, sharedStringsTablePackagePart);// 分析sharedStringsTable.xml,解析excel所有数据到readCacheanalysisSharedStringsTable(sharedStringsTablePackagePart.getInputStream(), xlsxReadWorkbookHolder);}XSSFReader xssfReader = new XSSFReader(pkg);analysisUse1904WindowDate(xssfReader, xlsxReadWorkbookHolder);// 设置样式表setStylesTable(xlsxReadWorkbookHolder, xssfReader);sheetList = new ArrayList<>();sheetMap = new HashMap<>();commentsTableMap = new HashMap<>();Map<Integer, PackageRelationshipCollection> packageRelationshipCollectionMap = MapUtils.newHashMap();xlsxReadWorkbookHolder.setPackageRelationshipCollectionMap(packageRelationshipCollectionMap);// 获取所有sheet页XSSFReader.SheetIterator ite = (XSSFReader.SheetIterator)xssfReader.getSheetsData();int index = 0;if (!ite.hasNext()) {throw new ExcelAnalysisException("Can not find any sheet!");}// 遍历sheet页while (ite.hasNext()) {InputStream inputStream = ite.next();// 保存所有sheet页sheetList.add(new ReadSheet(index, ite.getSheetName()));// 保存所有sheet页的输入流sheetMap.put(index, inputStream);if (xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT)) {CommentsTable commentsTable = ite.getSheetComments();if (null != commentsTable) {commentsTableMap.put(index, commentsTable);}}if (xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.HYPERLINK)) {PackageRelationshipCollection packageRelationshipCollection = Optional.ofNullable(ite.getSheetPart()).map(packagePart -> {try {return packagePart.getRelationships();} catch (InvalidFormatException e) {log.warn("Reading the Relationship failed", e);return null;}}).orElse(null);if (packageRelationshipCollection != null) {packageRelationshipCollectionMap.put(index, packageRelationshipCollection);}}index++;}}

    7.2 进入analysisSharedStringsTable方法,可以看到创建了一个SharedStringsTableHandler处理器

    7.3 再进入parseXmlSource看到xmlReader.setContentHandler(handler)这一行代码,设置了SharedStringsTableHandler处理器

private void parseXmlSource(InputStream inputStream, ContentHandler handler) {InputSource inputSource = new InputSource(inputStream);try {SAXParserFactory saxFactory;String xlsxSAXParserFactoryName = xlsxReadContext.xlsxReadWorkbookHolder().getSaxParserFactoryName();if (StringUtils.isEmpty(xlsxSAXParserFactoryName)) {saxFactory = SAXParserFactory.newInstance();} else {saxFactory = SAXParserFactory.newInstance(xlsxSAXParserFactoryName, null);}try {saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);} catch (Throwable ignore) {}try {saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);} catch (Throwable ignore) {}try {saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);} catch (Throwable ignore) {}SAXParser saxParser = saxFactory.newSAXParser();XMLReader xmlReader = saxParser.getXMLReader();xmlReader.setContentHandler(handler);xmlReader.parse(inputSource);inputStream.close();} catch (IOException | ParserConfigurationException | SAXException e) {throw new ExcelAnalysisException(e);} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {throw new ExcelAnalysisException("Can not close 'inputStream'!");}}}}

    7.4 我们将断点打在SharedStringsTableHandler里,发现下一步进入到这里面的startElement()    starteElement()后会调用endElement()

    7.5 反复调用,excel所有数据读取到readcache中

8. 调用.doRead()方法,开始SAX解析

    8.1 进入build()方法

    8.2 build()执行完后,执行read()方法,read()里还会进入一次read() 

9. 调用ExcelAnalyserImpl里的analysis()方法,设置sheetList,并调用执行器开始执行解析 

    9.1 调用的XlsxSaxAnalyser解析器execute()

 10. 进入parseXmlSource()方法,发现和之前的sax差不多,但只两次传入的handler类型不同,还是看一下传入的ContentHandler参数具体实现,进入XlsxRowHandler 内部

public class XlsxRowHandler extends DefaultHandler {private final XlsxReadContext xlsxReadContext;private static final Map<String, XlsxTagHandler> XLSX_CELL_HANDLER_MAP = new HashMap<String, XlsxTagHandler>(32);static {CellFormulaTagHandler cellFormulaTagHandler = new CellFormulaTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_FORMULA_TAG, cellFormulaTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_FORMULA_TAG, cellFormulaTagHandler);CellInlineStringValueTagHandler cellInlineStringValueTagHandler = new CellInlineStringValueTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler);CellTagHandler cellTagHandler = new CellTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_TAG, cellTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_TAG, cellTagHandler);CellValueTagHandler cellValueTagHandler = new CellValueTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_VALUE_TAG, cellValueTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_VALUE_TAG, cellValueTagHandler);CountTagHandler countTagHandler = new CountTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.DIMENSION_TAG, countTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_DIMENSION_TAG, countTagHandler);HyperlinkTagHandler hyperlinkTagHandler = new HyperlinkTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.HYPERLINK_TAG, hyperlinkTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_HYPERLINK_TAG, hyperlinkTagHandler);MergeCellTagHandler mergeCellTagHandler = new MergeCellTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.MERGE_CELL_TAG, mergeCellTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_MERGE_CELL_TAG, mergeCellTagHandler);RowTagHandler rowTagHandler = new RowTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.ROW_TAG, rowTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_ROW_TAG, rowTagHandler);}public XlsxRowHandler(XlsxReadContext xlsxReadContext) {this.xlsxReadContext = xlsxReadContext;}@Overridepublic void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(name);if (handler == null || !handler.support(xlsxReadContext)) {return;}xlsxReadContext.xlsxReadSheetHolder().getTagDeque().push(name);handler.startElement(xlsxReadContext, name, attributes);}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {String currentTag = xlsxReadContext.xlsxReadSheetHolder().getTagDeque().peek();if (currentTag == null) {return;}XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(currentTag);if (handler == null || !handler.support(xlsxReadContext)) {return;}handler.characters(xlsxReadContext, ch, start, length);}@Overridepublic void endElement(String uri, String localName, String name) throws SAXException {XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(name);if (handler == null || !handler.support(xlsxReadContext)) {return;}handler.endElement(xlsxReadContext, name);xlsxReadContext.xlsxReadSheetHolder().getTagDeque().pop();}}

    10.1 startElement()和endElement()都有多种实现

11. 进入用到的重要的几个类CellTagHandler、RowTagHandler

CellTagHandler: 读取cell的值,并放入tempCellData

public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) {XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder();xlsxReadSheetHolder.setColumnIndex(PositionUtils.getCol(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R),xlsxReadSheetHolder.getColumnIndex()));// t="s" ,it means String// t="str" ,it means String,but does not need to be read in the 'sharedStrings.xml'// t="inlineStr" ,it means String,but does not need to be read in the 'sharedStrings.xml'// t="b" ,it means Boolean// t="e" ,it means Error// t="n" ,it means Number// t is null ,it means Empty or NumberCellDataTypeEnum type = CellDataTypeEnum.buildFromCellType(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_T));xlsxReadSheetHolder.setTempCellData(new ReadCellData<>(type));xlsxReadSheetHolder.setTempData(new StringBuilder());// Put in data transformation informationString dateFormatIndex = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_S);int dateFormatIndexInteger;if (StringUtils.isEmpty(dateFormatIndex)) {dateFormatIndexInteger = DEFAULT_FORMAT_INDEX;} else {dateFormatIndexInteger = Integer.parseInt(dateFormatIndex);}xlsxReadSheetHolder.getTempCellData().setDataFormatData(xlsxReadContext.xlsxReadWorkbookHolder().dataFormatData(dateFormatIndexInteger));}@Overridepublic void endElement(XlsxReadContext xlsxReadContext, String name) {XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder();ReadCellData<?> tempCellData = xlsxReadSheetHolder.getTempCellData();StringBuilder tempData = xlsxReadSheetHolder.getTempData();String tempDataString = tempData.toString();CellDataTypeEnum oldType = tempCellData.getType();switch (oldType) {case STRING:// In some cases, although cell type is a string, it may be an empty tagif (StringUtils.isEmpty(tempDataString)) {break;}String stringValue = xlsxReadContext.readWorkbookHolder().getReadCache().get(Integer.valueOf(tempDataString));tempCellData.setStringValue(stringValue);break;case DIRECT_STRING:case ERROR:tempCellData.setStringValue(tempDataString);tempCellData.setType(CellDataTypeEnum.STRING);break;case BOOLEAN:if (StringUtils.isEmpty(tempDataString)) {tempCellData.setType(CellDataTypeEnum.EMPTY);break;}tempCellData.setBooleanValue(BooleanUtils.valueOf(tempData.toString()));break;case NUMBER:case EMPTY:if (StringUtils.isEmpty(tempDataString)) {tempCellData.setType(CellDataTypeEnum.EMPTY);break;}tempCellData.setType(CellDataTypeEnum.NUMBER);tempCellData.setNumberValue(BigDecimal.valueOf(Double.parseDouble(tempDataString)));break;default:throw new IllegalStateException("Cannot set values now");}if (tempCellData.getStringValue() != null&& xlsxReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) {tempCellData.setStringValue(tempCellData.getStringValue().trim());}tempCellData.checkEmpty();tempCellData.setRowIndex(xlsxReadSheetHolder.getRowIndex());tempCellData.setColumnIndex(xlsxReadSheetHolder.getColumnIndex());xlsxReadSheetHolder.getCellMap().put(xlsxReadSheetHolder.getColumnIndex(), tempCellData);}

RowTagHandler: 当一行读取完毕后,调用分析事件处理器,处理一行数据

                            xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext);

public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) {XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder();int rowIndex = PositionUtils.getRowByRowTagt(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R),xlsxReadSheetHolder.getRowIndex());Integer lastRowIndex = xlsxReadContext.readSheetHolder().getRowIndex();while (lastRowIndex + 1 < rowIndex) {xlsxReadContext.readRowHolder(new ReadRowHolder(lastRowIndex + 1, RowTypeEnum.EMPTY,xlsxReadSheetHolder.getGlobalConfiguration(), new LinkedHashMap<Integer, Cell>()));xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext);xlsxReadSheetHolder.setColumnIndex(null);xlsxReadSheetHolder.setCellMap(new LinkedHashMap<Integer, Cell>());lastRowIndex++;}xlsxReadSheetHolder.setRowIndex(rowIndex);}@Overridepublic void endElement(XlsxReadContext xlsxReadContext, String name) {XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder();RowTypeEnum rowType = MapUtils.isEmpty(xlsxReadSheetHolder.getCellMap()) ? RowTypeEnum.EMPTY : RowTypeEnum.DATA;// It's possible that all of the cells in the row are emptyif (rowType == RowTypeEnum.DATA) {boolean hasData = false;for (Cell cell : xlsxReadSheetHolder.getCellMap().values()) {if (!(cell instanceof ReadCellData)) {hasData = true;break;}ReadCellData<?> readCellData = (ReadCellData<?>)cell;if (readCellData.getType() != CellDataTypeEnum.EMPTY) {hasData = true;break;}}if (!hasData) {rowType = RowTypeEnum.EMPTY;}}xlsxReadContext.readRowHolder(new ReadRowHolder(xlsxReadSheetHolder.getRowIndex(), rowType,xlsxReadSheetHolder.getGlobalConfiguration(), xlsxReadSheetHolder.getCellMap()));xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext);xlsxReadSheetHolder.setColumnIndex(null);xlsxReadSheetHolder.setCellMap(new LinkedHashMap<>());}

12. 进入endRow()方法

      endRow()里通过getIgnoreEmptyRow()来判断是否忽略空行(true为忽略,默认为true)

     想要设置IgnoreEmptyRow为false,可以在最初使用EasyExcel时设置:

13. 进入dealData()方法

private void dealData(AnalysisContext analysisContext) {ReadRowHolder readRowHolder = analysisContext.readRowHolder();Map<Integer, ReadCellData<?>> cellDataMap = (Map)readRowHolder.getCellMap();readRowHolder.setCurrentRowAnalysisResult(cellDataMap);int rowIndex = readRowHolder.getRowIndex();int currentHeadRowNumber = analysisContext.readSheetHolder().getHeadRowNumber();boolean isData = rowIndex >= currentHeadRowNumber;// Last head columnif (!isData && currentHeadRowNumber == rowIndex + 1) {buildHead(analysisContext, cellDataMap);}// Now is datafor (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {try {if (isData) {readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext);} else {readListener.invokeHead(cellDataMap, analysisContext);}} catch (Exception e) {onException(analysisContext, e);break;}if (!readListener.hasNext(analysisContext)) {throw new ExcelAnalysisStopException();}}}

14. dealData()方法中的invoke方法会执行ModelBuildEventListener里的invoke()方法,在这里会进行单行数据到实体类的映射,buildStringList()真正执行映射

15. 退回XlsxSaxAnalyser的解析器execute()方法

      进入readComments()方法,读取额外信息(批注、超链接、合并单元格信息读取)

	private void readComments(ReadSheet readSheet) {if (!xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT)) {return;}CommentsTable commentsTable = commentsTableMap.get(readSheet.getSheetNo());if (commentsTable == null) {return;}Iterator<CellAddress> cellAddresses = commentsTable.getCellAddresses();while (cellAddresses.hasNext()) {CellAddress cellAddress = cellAddresses.next();XSSFComment cellComment = commentsTable.findCellComment(cellAddress);CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.COMMENT, cellComment.getString().toString(),cellAddress.getRow(), cellAddress.getColumn());xlsxReadContext.readSheetHolder().setCellExtra(cellExtra);xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext);}}

16. 最后进入.endSheet(xlsxReadContext)方法

@Overridepublic void endSheet(AnalysisContext analysisContext) {// 这里会调用所有监听器中的doAfterAllAnalysed方法,执行最后的操作for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {readListener.doAfterAllAnalysed(analysisContext);}}

17. 在读取完毕之后,执行finish()方法,关闭所有流 

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

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

相关文章

全流量安全分析之服务器非法外连

服务器非法外连监控的重要性在于保护服务器的安全性和防止被黑客利用&#xff0c;以下是几个重要的理由&#xff1a; 1、发现恶意活动&#xff1a;通过监控服务器的外连流量&#xff0c;可以及时发现是否有未经授权或可疑的连接尝试。这可能包括入侵攻击、数据泄露、恶意软件传…

小白vite+vue3搭建项目整个流程

第一步 查看npm 版本npm -v&#xff0c;npm版本是7&#xff0c;创建项目命令&#xff1a; npm create vitelatest threejsVue -- --template vue第二步 // 进入项目名为threejsVue的项目命令 cd threejsVue // 安装路由 npm install vue-router4 // 安装css npm install -D s…

手机号码格式校验:@Phone(自定义参数校验注解)

需求 新增接口 和 修改接口 中&#xff0c;手机号码的格式校验是普遍需要的。 在每个手机号码字段上添加正则表达式校验注解来实现校验&#xff0c;重复书写&#xff0c;容易出错&#xff1b;在不同的手机号码字段上&#xff0c;可能使用了不同的校验规则&#xff0c;无法有效…

【单片机】14-I2C通信之EEPROM

1.EEPROM概念 1.EEPROM 1.1 一些概念 &#xff08;1&#xff09;一些概念&#xff1a;ROM【只读存储器---硬盘】&#xff0c;RAM【随机访问存储器--内存】&#xff0c;PROM【可编程的ROM】&#xff0c;EPROM【可擦除ROM】&#xff0c;EEPROM【电可擦除ROM】 1.2 为什么需要EE…

gitlab配置webhook限制提交注释

一、打开gitlab相关配置项 vim /etc/gitlab/gitlab.rb gitlab_shell[custom_hooks_dir] "/etc/gitlab/custom_hooks" 二、创建相关文件夹 mkdir -p /etc/gitlab/custom_hooks mkdir -p /etc/gitlab/custom_hooks/post-receive.d mkdir -p /etc/gitlab/custom_h…

xcode15下载ios17模拟器失败

升级到xcode15后需要安装ios17模拟器 但是在下载过程中会遇到报错 如下图这种 网上搜索了一下发现有人遇到过无法下载的问题&#xff0c;并且在apple官网也有人提出类似问题 https://developer.apple.com/forums/thread/737648 解决方案就是从https://developer.apple.com/do…

Halcon中灰度直方图的使用与学习

目录 第一步:当前打开窗口的显示灰度图或者mono图片第二步:激活后,我们可以去调整调整右边直方图灰阶值的中蓝色和红色竖线,获取左边图上的灰阶值的范围内的特征显示。第三步:插入代码:总结:它的直观目的,就是查看灰度的分布情况!灰度直方图,是我们经常使用,抓取不同…

Mendix中的依赖管理:npm和Maven的应用

序言 在传统java开发项目中&#xff0c;我们可以利用maven来管理jar包依赖&#xff0c;但在mendix项目开发Custom Java Action时&#xff0c;由于目录结构有一些差异&#xff0c;我们需要自行配置。同样的&#xff0c;在mendix项目开发Custom JavaScript Action时&#xff0c;…

HBase高阶(一)基础架构及存储原理

一、HBase介绍 简介 HBase是Hadoop生态系统中的一个分布式、面向列的开源数据库&#xff0c;具有高可伸缩性、高性能和强大的数据处理能力。广泛应用于处理大规模数据集。 HBase是一种稀疏的、分布式、持久的多维排序map 稀疏&#xff1a;对比关系型数据库和非关系型数据库&a…

【软件工程_UML—StartUML作图工具】startUML怎么画interface接口

StartUML作图工具怎么画interface接口 初试为圆形 &#xff0c;点击该接口在右下角的设置中->Format->Stereotype Display->Label&#xff0c;即可切换到想要的样式 其他方式 在class diagram下&#xff0c;左侧有interface图标&#xff0c;先鼠标左键选择&#xff0…

MNIST手写数字识别

MNIST是一个手写体数字的图片数据集&#xff0c;该数据集由美国国家标准与技术研究所&#xff08;National Institute of Standards and Technology (NIST)&#xff09;发起整理&#xff0c;其包含 60,000 张训练图像和 10,000 张测试图像&#xff0c;每张图片的尺寸为 28 x 28…

Explain执行计划字段解释说明---ID字段说明

ID字段说明 1、select查询的序列号,包含一组数字&#xff0c;表示查询中执行select子句或操作表的顺序 2、ID的三种情况 &#xff08;1&#xff09;id相同&#xff0c;执行顺序由上至下。 &#xff08;2&#xff09;id不同&#xff0c;如果是子查询&#xff0c;id的序号会…

NEON优化:性能优化经验总结

NEON优化&#xff1a;性能优化经验总结 1. 什么是 NEONArm Adv SIMD 历史 2. 寄存器3. NEON 命名方式4. 优化技巧5. 优化 NEON 代码(Armv7-A内容&#xff0c;但区别不大)5.1 优化 NEON 汇编代码5.1.1 Cortex-A 处理器之间的 NEON 管道差异5.1.2 内存访问优化 Reference: NEON优…

大数据Flink(九十四):DML:TopN 子句

文章目录 DML:TopN 子句 DML:TopN 子句 TopN 定义(支持 Batch\Streaming):TopN 其实就是对应到离线数仓中的 row_number(),可以使用 row_number() 对某一个分组的数据进行排序 应用场景

APP或小程序突然打开显示连接网络失败,内容一片空白的原因是,SSL证书到期啦,续签即可

由于我们使用的是https&#xff0c;所以SSL证书到期了&#xff0c;通过https进入读取内容的APP或网站或小程序就会打开后连接网络失败&#xff0c;出现空白&#xff0c;这是因为我们申请的SSL证书到期了&#xff0c;因为我们申请的证书有效期有时是1个月或3个月&#xff0c;到期…

BI神器Power Query(26)-- 使用PQ实现表格多列转换(2/3)

实例需求&#xff1a;原始表格包含多列属性数据,现在需要将不同属性分列展示在不同的行中&#xff0c;att1、att3、att5为一组&#xff0c;att2、att3、att6为另一组&#xff0c;数据如下所示。 更新表格数据 原始数据表&#xff1a; Col1Col2Att1Att2Att3Att4Att5Att6AAADD…

【AI视野·今日NLP 自然语言处理论文速览 第四十二期】Wed, 27 Sep 2023

AI视野今日CS.NLP 自然语言处理论文速览 Wed, 27 Sep 2023 Totally 50 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Attention Satisfies: A Constraint-Satisfaction Lens on Factual Errors of Language Models Authors Mert …

Flutter开发之Package与Plugin

前言 在flutter中有包和插件两个概念&#xff0c;插件 (plugin) 是 package 的一种&#xff0c;全称是 plugin package&#xff0c;我们简称为 plugin&#xff0c;中文叫插件。包(Package)主要指对flutter相关功能的封装&#xff0c;类似于Android中的插件和iOS中的三方库。而插…

JVM机制理解与调优方案

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有需要我的支持&#xff0c;请私信或评论留言&#xff01; 前言 很多Java开发…

2023年9月随笔之摩托车驾考

1. 回头看 日更坚持了273天。 读《SQL学习指南&#xff08;第3版&#xff09;》更新完成 读《高性能MySQL&#xff08;第4版&#xff09;》持续更新 学信息系统项目管理师第4版系列持续更新 9月码字81307字&#xff0c;日均码字数2710字&#xff0c;累计码字451704字&…