1. 概览
在日常开发中,想必都遇到过批处理的需求,简单来说就是要求上传一个 Excel 文件,从 Excel 中读取数据,然后进行业务处理。对此,你怎么做呢?
使用 poi 一行行的读取数据,然后进行类型转换,最后调用业务方法。非常不错,恭喜你完成了这项工作。但,到此为止了吗?
1.1. 背景
经常会遇到这种情况,上线了一个需求,业务觉得不错,只是一条条录入效率太低,随后提出能不能搞一个 Excel 批量处理?当然,我们可以直接撸码,顺便加个小班,一气呵成。
简单回想一下,你有多久不处理手工的类型转换?
比如 Spring MVC 框架中,我们直接使用 JavaBean 来接收 request 请求的数据,那有没有一种方式可以从 Excel 中读取出 JavaBean 呢?
1.2. 目标
按照标准操作流程,框架需要支持:
-
从 Class 中读取信息生成 Excel 模板,该模板需要对部分数据进行保护,不允许用户修改;
-
用户获取 Excel 模板后,按要求完成相关信息填写,然后将 Excel 转换为 JavaBean 对象,快速完成业务操作;
-
前面两步需要支持嵌套 Bean,无需将关联对象的属性进行展开;
2. 快速入门
2.1. 添加 starter 及相关依赖
在 Spring boot 项目的 pom 中增加如下依赖:
<dependency>
<groupId>com.geekhalo.lego</groupId>
<artifactId>lego-starter-excelasbean</artifactId>
<version>0.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
2.2. 注入 ExcelAsBeanService 服务
在代码中直接注入 ExcelAsBeanService 对象,具体如下:
// 注入 excelAsBeanService
// 由 ExcelAsBeanAutoConfiguration 完成 ExcelAsBeanService 的注册
@Autowired
private ExcelAsBeanService excelAsBeanService;
2.3. 创建 Excel 模板
核心代码如下:
/**
* 创建 Excel 模板
* @param workbook
* @param sheetName
* @param dataCls
* @param <D>
*/
default <D> void writTemplateToSheet(HSSFWorkbook workbook, String sheetName, Class<D> dataCls){
writTemplateToSheet(workbook.createSheet(sheetName), dataCls);
}
/**
* 创建 Excel 模板
* @param sheet 待写入的 sheet
* @param dataCls 待写入数据
* @param <D>
*/
<D> void writTemplateToSheet(HSSFSheet sheet, Class<D> dataCls);
该方法,从 class 中读取信息,并将其写入到 Excel 模板。
2.4. 从 Excel 中读取并转换为 JavaBean
核心代码如下
/**
* 从 Sheet 中读取数据
* @param sheet 待读取数据的 Sheet
* @param dataCls 待读取数据的类型
* @param <D>
*/
default <D> List<D> readFromSheet(HSSFSheet sheet, Class<D> dataCls){
List<D> result = Lists.newArrayList();
readFromSheet(sheet, dataCls, d -> result.add(d));
return result;
}
/**
* 从 Sheet 中读取数据
* @param sheet 待读取数据的 Sheet
* @param dataCls 待读取数据的类型
* @param consumer 回调器,完成数据解析后,直接调用回调器
* @param <D>
*/
<D> void readFromSheet(HSSFSheet sheet, Class<D> dataCls, Consumer<D> consumer);
该方法,从 Sheet 中读取信息,并将其转换为 JavaBean;
2.5. 普通 bean 示例
首先,我们创建 JavaBean,并添加相关注解,具体如下:
@Data
public class CreateUserFromV1 implements CreateUserFrom{
@HSSFTemplateHeader(title = "姓名")
private String name;
@HSSFTemplateHeader(title = "出生日期")
private Date birthAt;
@HSSFTemplateHeader(title = "年龄")
private Integer age;
}
其中,@HSSFTemplateHeader 中的 title 为展示信息。
调用 writTemplateToSheet 方法,获取 Excel 如下:
如图可见,Excel 中有 姓名(name),出生日期(birthAt) 和 年龄(age) 三列,当试图对 Header 进行修改时,弹出提示信息“受保护区域,不允许操作”。
拿到模板后,在Excel中填入信息如下:
将该 Excel 上传服务器,调用 readFromSheet 方法,获取最终 Bean 如下:
2.6. 嵌套 bean 示例
首先,创建一个关联类,如下:
@Data
public class AddressForm {
@HSSFTemplateHeader(title = "省")
private String l1;
@HSSFTemplateHeader(title = "市")
private String l2;
@HSSFTemplateHeader(title = "区")
private String l3;
@HSSFTemplateHeader(title = "详细地址")
private String l4;
}
关联对象没有什么特别之处,只是增加了 @HSSFTemplateHeader 注解。
新建 Bean 如下:
@Data
public class CreateUserFromV2 implements CreateUserFrom{
@HSSFTemplateHeader(title = "姓名")
private String name;
@HSSFTemplateHeader(title = "出生日期")
private Date birthAt;
@HSSFTemplateHeader(title = "年龄")
private Integer age;
@HSSFEmbedded
private AddressForm addressForm;
}
也没有特殊之处,只是在 关联对象上增加了 @HSSFEmbedded 注解。
导出模板,查看效果:
如图所示,Excel 中新增 省、市、区、详细地址几列,用于数据输入。
同样,再拿到模板后,在Excel中填入信息如下:
将该 Excel 上传服务器进行处理,获取最终 Bean 如下:
3. 设计&扩展
3.1. 核心设计
整体设计如下:
-
Class 通过解析后获得一个 HSSFSheetReader,用于负责所有的读操作;
-
每个 HSSFSheetReader 提供 write template 和 read data 两组核心功能;
-
HSSFRowToBeanWriter 通过 ColumnToBeanPropertyWriter 完成每一列到Bean属性的映射;
-
write 模板核心流程如下:
-
为 Header Row 设置默认样式;
-
从 HSSFRowToBeanWriter 获取解析后的 title 信息,并写入 Cell
-
设置Header样式为保护模式,不允许任意修改;
-
reader data 流程如下:
-
从 Header 中获取 path 和 Index 的映射;
-
基于 ColumnToBeanPropertyWriter,从 Cell 中读取数据,并写入到 Bean 的属性;
-
回调消费函数,处理转换好的 Bean 对象;
3.2. 功能扩展
核心执行流程如下:
-
HSSFCellReader 从 Cell 中读取数据;
-
BeanPropertyWriter 将数据写入到 JavaBean 的属性;
装配流程如下:
-
String 以托管 bean 的方式,提供 CellReaderFactory 和 BeanPropertyWriterFactory 实现;
-
这些实现会注入到 HSSFCellReaderFactories 和 BeanPropertyWriterFactories,由 Factories 对其进行统一管理;
-
DefaultBeanPropertyWriterChainFactory 从 Factories 中获取合适的 HSSFCellReader 和 BeanPropertyWriter,将其封装为 BeanPropertyWriterChain;
4. 项目信息
项目仓库地址:https://gitee.com/litao851025/lego
项目文档地址:https://gitee.com/litao851025/lego/wikis/support/ExcelAsBean–%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5
原文始发于微信公众号(geekhalo):玩转 Excel 数据导入
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/60493.html