深入解析 EasyExcel 组件原理与应用

深入解析 EasyExcel 组件原理与应用

官方:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网

在日常的 Java 开发工作中,处理 Excel 文件的导入导出是极为常见的需求。
今天,咱们就一起来深入了解一款非常实用的操作 Excel 的组件 ——EasyExcel,看看它究竟有着怎样的神奇之处,能让众多开发者青睐有加。

一、引言

想必大家都知道,在 Java 领域中,Apache POI 是处理 Excel 文件的老牌工具了。但随着业务发展,面对越来越大的 Excel 文件读写需求时,POI 暴露出了一些诸如内存溢出(OOM)等问题,使用起来稍显吃力。而 EasyExcel 的出现,很好地弥补了这些不足,它基于 POI 进行了优化封装,为我们提供了更加简洁高效的 Excel 处理方式。接下来,咱们就详细剖析一下 EasyExcel 的原理以及它的具体应用。

二、EasyExcel 与 Apache POI 的渊源

EasyExcel 其实是站在 Apache POI 这个 “巨人的肩膀” 上发展起来的。Apache POI 基于 OOXML(Office Open XML)标准(像 xlsx、docx、pptx 等以指定格式的 xml 为基础并以 zip 格式压缩的文件)和 OLE2 标准(包括 xls、doc、ppt 等二进制格式的文件),只要符合上述标准的文件它都能进行读写操作。

对于 xlsx 文件来说,它基于 OOXML 标准,能够解压成对应的 xml 文件,在解析后的 xml 文件里,我们重点关注 sharedStrings.xml 与 sheet1.xml 这两个 xml 节点,因为里面存放着我们想要处理的 Excel 内容。

不过,POI 在处理 Excel 时有传统的 POM 解析和 SAX 解析两种方式。传统的 POM 解析会把 Excel 中的所有 XML 节点解析成一棵 DOM 树,整棵 DOM 树加载进内存,这样做虽然方便对每个 XML 节点进行随机访问,但很容易导致内存溢出现象,像仅仅 3 列的 Excel 文件在 7 万行左右就可能出现内存溢出问题。

而 SAX 解析则不同,它采用边读取边处理的方式操作 XML,逐行扫描 XML 文档,遇到标签时触发解析处理器,进而触发相应的事件 Handler。这种方式内存占用小、效率高,但 POI 官方提供的 API 过于繁琐,使用起来步骤繁多,比如读取一个 Excel 文件,要先将 xlsx 格式的 Excel 文件解析成 OPCPackage 对象,再创建 XSSFReader 对象来解析获取对应 xlsx 下的 xml 内容,想要处理还得进一步创建内容处理器等等,着实有些麻烦。

三、EasyExcel 的原理剖析

(一)核心架构与流程

在 EasyExcel 中,有一个核心的类叫 ExcelAnalyserImpl,它就像是整个操作的调度中心。里面包含了 AnalysisContext 类(分析上下文,存放对应的文件 Inputstream 跟 AnalysisEventListener 事件监听处理器等)和 ExcelReadExecutor 类(默认是 XlsxSaxAnalyser 类,核心方法是 parseXmlSource (),负责逐行 SAX 解析 Excel)。

当 XlsxSaxAnalyser 解析类执行解析方法后,会调用 AnalysisEventProcessor 执行对应的事件方法,而在这个 processor 里真正执行操作的就是对应的事件监听器啦。官方默认提供了一个事件监听器,能把 Excel 解析成 BeanMap 的形式,然后根据我们传入的实体类,返回给我们想要的实体类对象。要是我们有定制化的功能需求,那就需要去了解这个监听器的核心方法了哦。

以下是一个简单的示例代码,展示如何使用 EasyExcel 读取 Excel 文件并将数据转换为自定义对象(假设我们有一个 User 实体类,包含 name 和 age 等属性):

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

// 自定义监听器,用于处理读取到的数据
class UserDataListener extends AnalysisEventListener<User> {
    private List<User> userList = new ArrayList<>();

    // 每解析一行数据就会调用此方法
    @Override
    public void invoke(User user, AnalysisContext analysisContext) {
        userList.add(user);
    }

    // 全部解析完成后调用此方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里可以对读取到的所有数据进行后续操作,比如存入数据库等
        System.out.println("共读取到 " + userList.size() + " 条用户数据");
    }

    public List<User> getUserList() {
        return userList;
    }
}

public class EasyExcelReadExample {
    public static void main(String[] args) throws IOException {
        String filePath = "your_excel_file_path.xlsx";  // 替换为实际的 Excel 文件路径
        // 读取 Excel 文件,第二个参数是自定义的监听器
        EasyExcel.read(new FileInputStream(filePath), User.class, new UserDataListener()).sheet().doRead();
    }
}
注解

  • 在上述代码中,首先定义了 UserDataListener 类,它继承自 AnalysisEventListener,并重写了 invoke 和 doAfterAllAnalysed 方法。invoke 方法用于在每解析一行 Excel 数据时将其封装成 User 对象并添加到 userList 中,而 doAfterAllAnalysed 方法则在整个 Excel 文件解析完成后执行,可以在这里进行一些后续的数据处理操作,比如将数据批量存入数据库等。
  • 在 EasyExcelReadExample 类的 main 方法中,通过 EasyExcel.read 方法来启动 Excel 文件的读取流程,传入文件输入流、要转换的目标对象类型(这里是 User 类)以及自定义的监听器,然后调用 sheet 和 doRead 方法完成读取操作。

(二)关键方法解析

1. doRead () 方法
doRead () 方法实际调用过程还挺有意思的,它先是调用了 ExcelReader 中的 read 方法与 finish 方法。read 方法实际调用了 Executor 中的 execute 方法,这个 execute 方法里会调用前面提到的核心方法 parseXmlSource 解析 excel,并且在一个 sheet 中所有数据读取完后,会调用事件调度器的 endSheet 方法,让每一个监听器执行对应的 doAfterAllAnalysed 方法。而 finish 方法则负责对读取过程持有容器中流的内容输出,之后清理存储的缓存,把开启的相应的流关闭。
2. doWrite () 方法
doWrite () 方法同样调用了 Writer 中的 write 方法和 finish 方法,这里的 finish 方法作用和读操作里类似哦。write 方法实际调用的是 Builder 中的 addContent 方法,在 addContent 方法里又会调用 Executor 中的 add 方法,针对传入的列表,逐个对象执行 addOneRowOfDataToExcel 方法进行逐行写入。在这个过程中,无论是创建行对象前后,还是填充完数据之后,我们都可以自定义处理器对流程进行相应的处理呢,非常灵活。

以下是一个使用 EasyExcel 写入 Excel 文件的示例代码,假设我们要将一个 List<User> 数据写入到 Excel 中:

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import java.util.ArrayList;
import java.util.List;

class User {
    private String name;
    private int age;

    // 构造函数、Getter 和 Setter 方法省略,可根据实际情况补充完整

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class EasyExcelWriteExample {
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User("张三", 25));
        userList.add(new User("李四", 30));

        String filePath = "output_excel_file.xlsx";  // 输出的 Excel 文件路径

        // 这里使用 EasyExcel 的 write 方法开始写入操作
        EasyExcel.write(filePath, User.class).sheet("用户信息表").doWrite(userList);
    }
}
注解

  • 在这个示例中,先定义了 User 类来表示要写入 Excel 的数据对象,包含 name 和 age 等属性(这里省略了部分常规的方法,实际使用中需补充完整)。
  • 在 EasyExcelWriteExample 类的 main 方法中,创建了一个 List<User> 集合并添加了一些示例数据。然后通过 EasyExcel.write 方法启动写入流程,传入输出文件路径和要写入的数据对象类型(User 类),接着指定 sheet 名称,最后调用 doWrite 方法并传入数据列表,这样就可以将数据成功写入到指定的 Excel 文件中啦。

四、EasyExcel 的性能与易用性优势

(一)性能优势

通过官方给出的效果图可以看到,EasyExcel 仅用 16M 内存在 23 秒内就能读取 75M(46W 行 25 列)的 Excel 文件,相比之下,POI 读取内存要用到 100M 往上了,读取时间更是没法比,可见在性能方面,EasyExcel 是遥遥领先呀。这得益于它基于事件机制逐行扫描数据,对数据逐行处理后,只要对象不被引用,就会被 JVM 快速垃圾回收,从而有效避免了 OOM 问题。

(二)易用性优势

使用 EasyExcel 进行文件的导入导出,相比 POI 的流程那可是简化了一大截呢,只要短短几行代码就可以轻松实现 Excel 的导入导出。这对于咱们开发者来说,简直太友好了,能大大提高开发效率呀。

五、EasyExcel 中的设计模式 个人理解

(一)工厂模式

EasyExcel 里的 EasyExcelFactory 类就运用了工厂模式哦。这个类拥有很多对应方法的不同入参的变体,最终目的都是为了根据不同的入参构造对应的 ExcelWriter/ReaderBuilder 对象,让代码调用起来简洁又美观呢。

(二)观察者模式(也叫发布 - 订阅模式)

通过事件监听器的机制,实现了类似观察者模式的效果。当 Excel 文件解析过程中遇到行、单元格等情况时会触发事件,然后通过自定义监听器来处理数据,各个监听器就像是观察者一样,等待着对应的事件发生然后执行相应的操作。

(三)模板方法模式

在解析和写入等流程中,定义了一些模板化的方法,不同的实现类可以按照这些模板去扩展和定制具体的业务逻辑,保证了整体流程的规范性和可扩展性。

(四)装饰器模式

在写样式等方面运用了装饰器模式,方便我们对 Excel 的样式等进行个性化的装饰和处理,让生成的 Excel 文件更加符合实际业务需求。

六、实际应用案例

在实际项目中,EasyExcel 发挥着巨大的作用呢。比如说,我曾经需要从 Excel 表格中批量导入用户信息到 MySQL 数据库里。如果使用 POI,不仅操作复杂,而且面对稍大一点的 Excel 文件就容易出现内存溢出问题。而选择 EasyExcel 后,它简单易用,性能又优越,更重要的是能够节约内存、解决大文件内存溢出问题。像一个 3M 的 excel 文件,用 POI sax 解析依然需要 100M 左右内存,改用 easyexcel 后可以降低到几 M,而且再大的 excel 文件也不会出现内存溢出啦。对于数据量小的文件,我采用同步模式一次性获取所有表格数据并存储到 List 中;对于数据量大的文件,则采用自定义 Listener 的方式异步逐行读取 Excel 并分批插入到数据库中,非常方便高效。

七、总结

总的来说,EasyExcel 作为一款强大的 Excel 处理组件,在 Apache POI 的基础上进行了优化和封装,无论是在性能、易用性还是功能扩展性上都有着出色的表现。通过运用多种设计模式,让它更加灵活且易于使用,在实际项目中能够帮我们轻松应对各种 Excel 文件的读写需求,大大提高了开发效率,也减少了因为内存溢出等问题带来的烦恼。希望大家在之后的开发工作中,如果遇到 Excel 处理相关的业务,不妨试试 EasyExcel 哦,相信它会给你带来意想不到的惊喜。

以上就是本次关于 EasyExcel 的全部内容分享啦,大家如果有任何疑问或者使用心得,欢迎在评论区留言交流呀。

觉得有用的话可以点点赞 (*/ω\*),支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Java Springboot高校教室资源管理系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&#xff1a;…

k8s1.31版本最新版本集群使用容器镜像仓库Harbor

虚拟机 rocky9.4 linux master node01 node02 已部署k8s集群版本 1.31 方法 一 使用容器部署harbor (1) wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo yum -y install docker-ce systemctl enable docker…

C语言数据结构学习:循环队列

C语言 数据结构学习 汇总入口&#xff1a; C语言数据结构学习&#xff1a;[汇总] 1. 循环队列 队列的博客&#xff1a;C语言数据结构学习&#xff1a;队列 循环队列会预先定义最大队列空间&#xff0c;然后定义一个数组&#xff0c;通过队列头和队列尾指针分别指向开头和结尾&…

Vue——响应式数据,v-on,v-bind,v-if,v-for(内含项目实战)

目录 响应式数据 ref reactive 事件绑定指令 v-on v-on 鼠标监听事件 v-on 键盘监听事件 v-on 简写形式 属性动态化指令 v-bind iuput标签动态属性绑定 img标签动态属性绑定 b标签动态属性绑定 v-bind 简写形式 条件渲染指令 v-if 遍历指令 v-for 遍历对象的值 遍历…

小米note pro一代(leo)线刷、twrp、magisk、TODO: android源码编译

本文主要说android5 整体思路 android 5.1 twrp magisk Zygisk(Riru) Dreamland(xposed) Riru不支持android5.1, 因此只能选择Zygisk : 如果你正在使用 Android 5&#xff0c;你必须使用 Zygisk 因为 Riru 并不支持 Android 5. 基于magisk之上的xposed 其中提到的 作者…

自然语言处理: RAG优化之Embedding模型选型重要依据:mteb/leaderboard榜

本人项目地址大全&#xff1a;Victor94-king/NLP__ManVictor: CSDN of ManVictor git地址&#xff1a;https://github.com/opendatalab/MinerU 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路…

Redis 常用数据类型插入性能对比:循环插入 vs. 批量插入

Redis 是一款高性能的键值数据库&#xff0c;其支持多种数据类型&#xff08;String、Hash、List、Set、ZSet、Geo&#xff09;。在开发中&#xff0c;经常会遇到需要插入大量数据的场景。如果逐条插入&#xff0c;性能会显得较低&#xff0c;而采用 Pipeline 批量插入 能大幅提…

oneplus6线刷、trwp、magisk(apatch)、LSPosed、Shamiko、Hide My Applist

oneplus6线刷android10.0.1 oneplus6线刷包(官方android10.0.1)下载、线刷教程&#xff1a; OnePlus6-brick-enchilada_22_K_52_210716_repack-HOS-10_0_11-zip 启用开发者模式 设置 / 连续点击6次版本号 : 启用开发者模式设置/开发者模式/{打开 usb调试, 打开 网络adb调试,…

node.js中使用express.static()托管静态资源

express.static()定义 express.static(root, [options])是一个中间件函数&#xff0c;负责为Express应用提供静态资源服务。它允许你指定一个或多个目录作为静态资源的根目录&#xff0c;当客户端请求这些资源时&#xff0c;Express会查找并返回对应的文件。 安装express npm i…

【含开题报告+文档+PPT+源码】基于SSM的社区老人服务系统设计与实现

开题报告 在当前人口老龄化趋势明显以及信息化社会发展背景下&#xff0c;基于 SSM 框架构建的社区老人服务系统具有深远的背景意义。首先&#xff0c;它响应了我国老龄化进程加快所带来的多元化、个性化养老服务需求&#xff0c;利用互联网技术为老年人提供便捷高效的在线申请…

Spring AI 框架使用的核心概念

一、模型&#xff08;Model&#xff09; AI 模型是旨在处理和生成信息的算法&#xff0c;通常模仿人类的认知功能。通过从大型数据集中学习模式和见解&#xff0c;这些模型可以做出预测、文本、图像或其他输出&#xff0c;从而增强各个行业的各种应用。 AI 模型有很多种&…

学习与理解LabVIEW中多列列表框项名和项首字符串属性

多列列表框控件在如下的位置&#xff1a; 可以对该控件右击&#xff0c;如下位置&#xff0c;即可设置该控件的显示项&#xff1a; 垂直线和水平线指的是上图中组成单元格的竖线和横线&#xff08;不包括行首列首&#xff09; 现在介绍该多列列表框的两个属性&#xff0c;分别…

(Keil)MDK-ARM各种优化选项详细说明、实际应用及拓展内容

参考 MDK-ARM各种优化选项详细说明、实际应用及拓展内容 本文围绕MDK-ARM优化选项,以及相关拓展知识(微库、实际应用、调试)进行讲述,希望对你今后开发项目有所帮助。 1 总述 我们所指的优化,主要两方面: 1.代码大小(Size) 2.代码性能(运行时间) 在MDK-ARM中,优…

实时数据开发 | 怎么通俗理解Flink容错机制,提到的checkpoint、barrier、Savepoint、sink都是什么

今天学Flink的关键技术–容错机制&#xff0c;用一些通俗的比喻来讲这个复杂的过程。参考自《离线和实时大数据开发实战》 需要先回顾昨天发的Flink关键概念 检查点&#xff08;checkpoint&#xff09; Flink容错机制的核心是分布式数据流和状态的快照&#xff0c;从而当分布…

[译]Elasticsearch Sequence ID实现思路及用途

原文地址:https://www.elastic.co/blog/elasticsearch-sequence-ids-6-0 如果 几年前&#xff0c;在Elastic&#xff0c;我们问自己一个"如果"问题&#xff0c;我们知道这将带来有趣的见解&#xff1a; "如果我们在Elasticsearch中对索引操作进行全面排序会怎样…

七、SElinux

一、SElinux简介 SELinux是Security-Enhanced Linux的缩写&#xff0c;意思是安全强化的linuxSELinux 主要由美国国家安全局(NSA)开发&#xff0c;当初开发的目的是为了避免资源的误用传统的访问控制在我们开启权限后&#xff0c;系统进程可以直接访问当我们对权限设置不严谨时…

鸿蒙开发-音视频

Media Kit 特点 一般场合的音视频处理&#xff0c;可以直接使用系统集成的Video组件&#xff0c;不过外观和功能自定义程度低Media kit&#xff1a;轻量媒体引擎&#xff0c;系统资源占用低支持音视频播放/录制&#xff0c;pipeline灵活拼装&#xff0c;插件化扩展source/demu…

小程序25- iconfont 字体图标的使用

项目中使用到图标&#xff0c;一般由公司设计进行设计&#xff0c;设计好后上传到阿里巴巴矢量图标库 日常开发过程中&#xff0c;也可以通过 iconfont 图标库下载使用自带的图标 补充&#xff1a;使用 iconfont 图标库报错&#xff1a;Failed to load font 操作步骤&#xff…

vulhub之fastjson

fastjson 1.2.24 反序列化 RCE 漏洞(CVE-2017-18349) 漏洞简介 什么是json json全称是JavaScript object notation。即JavaScript对象标记法,使用键值对进行信息的存储。举个简单的例子如下: {"name":"BossFrank", "age":23, "isDevel…

Java语言程序设计 选填题知识点总结

第一章 javac.exe是JDK提供的编译器public static void main (String args[])是Java应用程序主类中正确的main方法Java源文件是由若干个书写形式互相独立的类组成的Java语言的名字是印度尼西亚一个盛产咖啡的岛名Java源文件中可以有一个或多个类Java源文件的扩展名是.java如果…