最近太忙了。刚回到家,抽空补了一篇文章。不知道能不能帮到你。最近在研究Excel的导入功能。该产品要求导入的数据在存储到数据库之前进行验证。于是就简单封装了一个工具,兄弟们用了之后都觉得不错,今天分享一下自己的想法。easyexcel库我们都知道POI是Java操作Excel的基础库。它尚未针对多功能性进行定制,并且存在一些限制。经过一番研究,决定使用二次封装库easyexcel进行业务开发。com.alibabaeasyexcel${easyexcel.version}easyexcel将读取Excel的生命周期抽象为几个阶段,方便我们在每个阶段注入你想要实现的逻辑。这些阶段包含在ReadListener接口中publicinterfaceReadListenerextendsListener{/***当任何一个侦听器执行错误报告时,所有侦听器都会收到此方法。如果这里抛出异常,整个读取就会终止。*这里是**@paramexception*@paramcontext*@throwsException*/voidonException(Exceptionexception,AnalysisContextcontext)throwsException;/***读取每一行的excel表头时会执行该方法**@paramheadMap*@paramcontext*/voidinvokeHead(MapheadMap,AnalysisContextcontext);/***每行数据读取时都会执行此方法**@paramdata*onerowvalue.Isissameas{@linkAnalysisContext#readRowHolder()}*@paramcontext*analysiscontext*/voidinvoke(Tdata,AnalysisContextcontext);/***如果有额外的cell信息返回,使用该方法处理**@paramextra*extrainformation*@paramcontext*analysiscontext*/voidextra(CellExtraextra,AnalysisContextcontext);/***整个excelsheet解析完成后执行的逻辑。**@paramcontext*/voiddoAfterAllAnalysed(AnalysisContextcontext);/***用于控制是否读取下一行的策略**@paramcontext*@return*/booleanhasNext(AnalysisContextcontext);}它的抽象实现AnalysisEventListener提供了一个更抽象的满足需求,我会进一步实现这个抽象,实现Excel的导入和校验。当你了解了一个框架的抽象接口之后,试着看看它是否有满足你需求的实现。另外,这里多说一点,界面中的AnalysisContext包含了很多有用的contextualmeta信息,比如当前行,当前配置策略,excel整体结构等信息,你可以调用这些信息需要的时候。JSR303验证一开始写了一个抽象的验证工具,最后发现每个字段都需要写它具体的验证逻辑。如果一个Excel中的字段数量激增,这对开发来说可能是一场噩梦。这让我想起了业界已有的规范——JSR303验证规范,将数据模型(Model)和验证(Validation)分别抽象出来,非常灵活,工作量明显减少。我们只需要找个地方和esayexcel生命周期结合即可。我们只需要在SpringBoot项目中引入如下依赖即可集成JSR303验证:>JSR303相关教程,可以查看我的文章。在实现过程中,我们可以在解析各个字段的时候进行校验,对应ReadListener的invoke(Tdata,AnalysisContextcontext)方法。该方法可以实现字段校验触发约束时停止excel解析的策略;另一个Validation可以在Excel分析完成后进行,对应doAfterAllAnalysed(AnalysisContextcontext)。这里我们以第二种为例来实现。我们在写代码的时候,尽量做到职责单一,尽量一个类或者一个方法只做一件事,这样你的代码就足够清晰了。编写校验处理类这里我把解析和校验分开实现,先写JSR303校验工具。这里假设已经有验证器javax.validation.Validator的实现,后面会讲到这个实现注入到哪里。importcn.felord.validate.Excel;importlombok.AllArgsConstructor;importorg.springframework.util.StringUtils;importjavax.validation.ConstraintViolation;importjavax.validation.Validator;importjava.util.*;importjava.util.stream.Collectors;/***excel校验工具**@paramthetypeparameter*@authorfelord.cn*@since2021/4/1414:14*/@AllArgsConstructorpublicclassExcelValidator{privatefinalValidatorvalidator;privatefinalIntegerbeginIndex;/***集合校验**@paramdata待校验集合*@returnlist*/publicListvalidate(Collectiondata){intindex=beginIndex+1;Listmessages=newArrayList<>();for(Tdatum:data){Stringvalidated=this.doValidate(index,datum);if(StringUtils.hasText(validated)){messages.add(validated);}index++;}returnmessages;}/***这里是validation的基本方法**@paramindex本文数据为位于*@paramdata一条待校验数据的行号*@return会提示数据校验异常,如果有是触发验证规则的验证规则,会封装提示信息。*/privateStringdoValidate(intindex,Tdata){//这里使用了JSR303的验证器,同时使用了分组验证。Excel为组标识Set>validate=validator.validate(data,Excel.class);returnvalidate.size()>0?"line"+index+",triggerconstraint:"+validate.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(",")):"";}}以上就是整个验证的逻辑。如果验证通过则不提示任何信息,如果验证不通过则封装验证约束信息返回。这里的Validator从哪里来?SpringBoot集成JSR303时,会自动在SpringIoC中注入一个Validator实现,我们就可以使用了。实现AnalysisEventListener完全是easyexcel的一个功能。我们只需要实现开头提到的Excel抽象分析监听器接口AnalysisEventListener,将分析字段添加到集合中,分析完成后再进行校验即可。这里如果验证失败,会抛出携带验证信息的异常,异常会被处理返回给前端提示。请记住:不能将AnalysisEventListener的实现注入到SpringIoC中。importcn.hutool.json.JSONUtil;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importcn.felord.exception.ServiceException;importorg.springframework.util.CollectionUtils;importjava.util.ArrayList;importjava.util.Collection;importjava.util.List;importjava.util.function.Consumer;/***这个类不能被Spring托管**@paramthetypeparameter*@authorfelord.cn*@since2021/4/1414:19*/publicclassJdbcEventListenerextendsAnalysisEventListener{/***Excel总数阈值*/privatestaticfinalIntegerMAX_SIZE=10000;/***验证工具*/privatefinalExcelValidatorexcelValidator;/***如果验证excel消费分析得到的数据*/privatefinalConsumer>batchConsumer;/***解析数据的临时存储容器*/privatefinalListlist=newArrayList<>();/***实例化sanewJdbceventlistener.**@paramexcelValidatorExcel校验工具*@parambatchConsumerExcel分析结果批量消费工具,可实现为写入数据库等消费操作*/publicJdbcEventListener(ExcelValidatorexcelValidator,Consumer<Collection>batchConsumer){this.excelValidator=excelValidator;this.batchConsumer=batchConsumer;}@OverridepublicvoidonException(Exceptionexception,AnalysisContextcontext)throwsException{list.clear();throwexception;}@Overridepublicvoidinvoke(Tdata,Analysis/Contextcontext){如果没有超过阈值,则将解析后的excel字段添加到集合中);}@OverridepublicvoiddoAfterAllAnalysed(AnalysisContextcontext){//在所有分析完成后验证并使用集合if(!CollectionUtils.isEmpty(this.list)){Listvalidated=this.excelValidator.validate(this.list);if(CollectionUtils.isEmpty(validated)){this.batchConsumer.accept(this.list);}else{thrownewServiceException(JSONUtil.toJsonStr(validated));}}}}封装最终的工具这里,参考文档esayexcel封装成一个通用的Excel阅读工具importcom.alibaba.excel.EasyExcel;importlombok.AllArgsConstructor;importlombok.Data;importjavax.validation.Validator;importjava.io.InputStream;importjava.util.Collection;importjava.util.function.Consumer;/***excel阅读工具**@authorfelord.cn*@since2021/4/1415:10*/@AllArgsConstructorpublicclassExcelReader{privatefinalValidatorvalidator;/***ReadExcel.**@param类型参数*@parammetathemeta*/publicvoidread(Metameta){ExcelValidatorexcelValidator=newExcelValidator<>(validator,meta.headRowNumber);JdbcEventListenerreadListener=newJdbcEventListener<>(excelValidator,meta.consumer);EasyExcel.read(meta.excelStream,meta.domain,readListener).headRowNumber(meta.headRowNumber).sheet().doRead();}/***解析需要的元数据**@paramthetypeparameter*/@DatapublicstaticclassMeta{/***excel文件流*/privateInputStreamexcelStream;/***excel表头行号,参考easyexcelapi和你的实际情况*/privateIntegerheadRowNumber;/***对应excel的封装数据类,需要参考easyexcel教程*/privateClassdomain;/***分析结果消费函数*/privateConsumer>consumer;}}为了方便起见,我们将这个工具注入到SpringIoC中/***Excel阅读工具**@paramvalidatorthevalidator*@returntheexcelreader*/@BeanpublicExcelReaderexcelReader(Validatorvalidator){returnnewExcelReader(validator);}写接口这里Excel的数据类ExcelData就不详细说了,太简单了!去esayexcel的文档就可以了。写一个SpringMVC接口的例子,就是这么简单。@AutowiredprivateExcelReaderexcelReader;@AutowiredprivateDataServicedataService;@PostMapping("/excel/import")publicRest>importManufacturerInfo(@RequestPartMultipartFilefile)throwsIOException{InputStreaminputStream=file.getInputStream();ExcelReader.MetaexcelDataMeta=;excelDataMeta.setExcelStream(inputStream);excelDataMeta.setDomain(ExcelData.class);excelDataMeta.setHeadRowNumber(2);//批量写入数据库的逻辑excelDataMeta.setConsumer(dataService::saveBatch);this.excelReader.read(excelDataMeta);returnRestBody.ok();}总结今天我演示了如何结合easyexcel和JSR303。其实原理很简单。您只需要找到这两种技术的结合点并将它们结合起来即可。你学会了吗?本文转载自微信公众号“码农小胖哥”,可通过以下二维码关注。转载本文请联系码农小胖公众号。