本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接
最近在做Excel导入功能,产品要求对导入数据先进行校验然后再入库。于是简单封装了一个工具,结果兄弟们用了都说好,今天就把思路分享出来。
easyexcel 库
我们都知道
POI
是Java操作Excel的基础库。为了通用性并没有做定制,而且还有一些局限性。经过一番调研决定采用二次封装库
easyexcel
来进行业务开发。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
复制代码
easyexcel
将读取Excel的生命周期抽象为了几个阶段,方便我们在各个阶段注入你想要实现的逻辑。 这几个阶段包含在
ReadListener
接口中
public interface ReadListener<T> extends Listener {
/**
* 当任何一个侦听器执行错误报告时,所有侦听器都将接收此方法。 如果在此处引发异常,则整个读取将终止。
* 这里是处理读取excel异常的
*
* @param exception
* @param context
* @throws Exception
*/
void onException(Exception exception, AnalysisContext context) throws Exception;
/**
* 读取每行excel表头时会执行此方法
*
* @param headMap
* @param context
*/
void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context);
/**
* 读取每行数据的时候回执行此方法
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
* analysis context
*/
void invoke(T data, AnalysisContext context);
/**
* 如果有额外的单元格信息返回就用此方法处理
*
* @param extra
* extra information
* @param context
* analysis context
*/
void extra(CellExtra extra, AnalysisContext context);
/**
* 在整个excel sheet解析完毕后执行的逻辑。
*
* @param context
*/
void doAfterAllAnalysed(AnalysisContext context);
/**
* 用来控制是否读取下一行的策略
*
* @param context
* @return
*/
boolean hasNext(AnalysisContext context);
}
复制代码
其抽象实现
AnalysisEventListener<T>
提供更加符合需要的抽象,我会进一步实现这个抽象来实现Excel的导入和校验。
在你了解一个框架的抽象接口后,尽量要去看一下它有没有能满足你需要的实现。
另外这里要多说一点,接口中的
AnalysisContext
包含了很多有用的上下文元信息,比如 当前行、当前的配置策略、excel整体结构等信息,你可以在需要的时候调用这些信息。
JSR303校验
最开始自己写了一个抽象的校验工具,最后发现每一个字段都要编写其具体的校验逻辑,如果一个Excel的字段量爆炸,这对开发来说就可能是噩梦。这使我想到了业界已经有的规范-
JSR303
校验规范,它将数据模型(Model)和校验(Validation)各自抽象,非常灵活,而且工作量明显降低。我们只需要找到和
esayexcel
生命周期结合的地方就行了。我们只需要引入以下依赖就能在Spring Boot项目中集成JSR303校验:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
复制代码
关于JSR303相关的教程可以查看我另一篇文章
思路
我们可以在解析每个字段的时候校验,这对应
ReadListener
的
invoke(T data, AnalysisContext context)
方法,这种方式可以实现当字段校验触发约束时就停止excel解析的策略;另一种可以在Excel解析完毕后执行校验,对应
doAfterAllAnalysed(AnalysisContext context)
。这里以第二种为例我们来实现一下。
我们在编写代码时,尽量职责单一,一个类或者一个方法尽量只干一个事,这样让自己的代码足够清晰。
编写校验处理类
这里我把解析和校验分开实现,先编写JSR303校验工具。这里假设已经有了校验器
javax.validation.Validator
的实现,
稍后我会讲这个实现从哪里注入
。
import cn.felord.validate.Excel;
import lombok.AllArgsConstructor;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.*;
import java.util.stream.Collectors;
/**
* excel 校验工具
*
* @param <T> the type parameter
* @author felord.cn
* @since 2021 /4/14 14:14
*/
@AllArgsConstructor
public class ExcelValidator<T> {
private final Validator validator;
private final