EasyExcel 归档之后,该迁移到 Apache Fesod 吗?
前言
组里上周有人问,EasyExcel 都归档了,我们是不是该换个库。问题本身不奇怪,我花了两天把几个仓库、POM 和官方文档翻了一遍,最后给组里的答复是:不用换,至少现在不用。
顺带说一句,这类话题里转来转去的版本号、坐标和协议,错得比想象中多,转述几手之后细节就走样了。下面只写我自己核对过的东西:这三个库各自是什么、关系怎么变的、真要迁代码要动哪里,以及我们自己的项目为什么决定先不动。
一、EasyExcel 为什么停了
EasyExcel 是阿里 2018 年开源的。它当年能起来,靠的是一个实打实的技术点:用 SAX 流式解析替代「把整个工作簿读进内存」。在这之前,POI 的默认用法是 user model,几十万行直接把堆顶爆。EasyExcel 改成事件回调逐行读,内存占用和文件大小解耦,报表导出、批量导入这种场景一下就顺了。它后来成为这类需求的首选,原因就在这。
把这两条路线摊开看会更清楚。POI 的 user model 会把整个工作簿解析成 XSSFWorkbook、Sheet、Row、Cell 这一棵对象树,全部驻留内存,行数一多就撑不住。SAX 反过来,把文件当成一串事件:开始一行、读一个单元格、结束一行。你注册回调,框架按顺序推给你,同一时刻内存里只有当前这一行。差别不在写法好不好看,而在内存占用能不能和文件大小脱钩。几十万行的导出,只有后者跑得动。
转折出现在核心作者身上。大约 2023 年,EasyExcel 的核心作者离开阿里。作者一走,项目基本进入事实上的停滞。停滞期的表现很具体:issue 有人提,但很少看到维护者回应,PR 挂着不动,版本号长期不跳。对一个已经被大量项目引用的库来说,这种状态能拖上好几年,因为存量用户不会因为不更新就立刻搬走。
最后一个版本是 4.0.3,2024 年 9 月发布。这次升级把底层 POI 从 4.1.2 提到了 5.2.5,并且支持 JDK 21。也就是说,它在停止维护之前,做的最后一件事恰好是把「JDK 21 上能不能用」这个问题解决掉。再往后,2025 年 9 月 4 日,alibaba/easyexcel 仓库被归档,变成只读。
归档这件事本身也不难理解。一个开源项目的主要维护者离开,如果没有组织接手,代码放在那里慢慢冷却,是挺常见的结果。与其挂着一个没人管的仓库,不如把它标成只读,至少让后来的人一眼看出它的状态。对使用者来说,这比一个看起来还在更新、实际没人维护的库要诚实。
关于归档,我想把话说清楚一点。归档不等于有漏洞,归档只等于不再有新功能。判断一个冻结的依赖能不能继续留,我会看三件事:当前版本有没有被公开的 CVE 影响,它的底层依赖有没有同样的问题,以及它是否还覆盖得住现有需求。这三条过了,归档这个状态本身就不构成换库的理由。把归档直接等同于必须换,是挺常见的误解。
二、接棒的是 FastExcel
EasyExcel 停了之后,接着往下做的是 FastExcel。主导的人是庄家钜,他本来就是 EasyExcel 的维护者之一,不是外面随便来的人。
坐标是 cn.idev.excel:fastexcel,最新版本 1.3.0,2025 年 8 月发布。它不是随手 fork 出来的一条分支,API 和 EasyExcel 保持兼容。这个兼容性挺关键:从 EasyExcel 迁到 FastExcel,绝大多数调用只需要改 import 和入口类,业务逻辑不用重写。这也是它能在短时间内被接受的原因。
API 兼容这四个字落到代码上,就是包名和入口类对得上,方法签名基本一致,链式调用的写法也一样。它不是把 EasyExcel 推倒重写一遍再换个名字,而是同一套调用方式往下延续。所以迁移的主要成本不在理解新 API,而在把散落各处的 import 换掉。
协议这里要提一下,FastExcel 是 Apache-2.0。不少人下意识以为它是 MIT,其实是错的。对企业项目来说协议不是小事,Apache-2.0 和 MIT 虽然都算宽松协议,但在专利授权、商标和衍生作品的处理上有差别。走合规流程的时候,协议写错会让整个评估建在一个错误的前提上。顺便说,EasyExcel、FastExcel、Fesod 这三个全部是 Apache-2.0。
还有一个细节能说明「捐给 Apache」这件事是真的:github.com/fast-excel/fastexcel 现在会重定向到 apache/fesod。一个个人组织下的仓库指向了 Apache 基金会下的仓库,这就是代码归属发生转移的直接痕迹。
由维护者接手,比被完全陌生的人 fork 让人放心一些。他熟悉原来的设计取舍,也知道哪些历史包袱不能动。走 API 兼容这条路线本身就说明,他想的不是另起炉灶,而是把原来那套东西接着维护下去。
三、Apache Fesod
Apache Fesod 是捐给 ASF 之后的名字。podling 的起始时间是 2025 年 9 月 17 日,仓库在 github.com/apache/fesod。
Fesod 这个名字是 “fast easy spreadsheet and other documents” 的缩写,快速、简单的电子表格和其他文档。名字本身就说明了定位:EasyExcel 那套快和简单的体验保留,同时往其他文档格式扩展。
它和 POI 的关系容易被搞混,这里说清楚。Fesod 是一个独立的 Apache 孵化项目,不是 Apache POI 的子项目,它只是把 POI 5.5.1 当成底层的读写引擎来用。这两者一个是上层封装,一个是底层引擎,Fesod 有自己的 committer、自己的 podling、自己的发版节奏。把它当成 POI 的子项目,会让人误以为它的路线图和治理挂在 POI 下面,实际上不是。
坐标方面,artifact 是 org.apache.fesod:fesod-sheet。注意,并不存在一个叫 org.apache.fesod:fesod 的 artifact,照这个坐标去写 POM,Maven 会直接报找不到依赖,编译都进不去。它的兄弟 artifact 还有 fesod-common、fesod-bom、fesod-parent、fesod-shaded、fesod-examples。
入口类是 FesodSheet,包根在 org.apache.fesod.sheet,工具类在 org.apache.fesod.common。官方文档里给的就是 FesodSheet.read 和 FesodSheet.write。
版本目前走到三个:
- 2.0.0-incubating,2026 年 1 月
- 2.0.1-incubating,2026 年 2 月 11 日
- 2.0.2-incubating,2026 年 5 月 30 日,最新
真要用它,有两个坑得先知道。
第一,IP 清理还在进行。Fesod 的 POM 里写了,从 Alibaba EasyExcel 派生过来的代码,许可证头更新和 IP 清理工作仍在进行中。这是 Apache 孵化项目的常规动作,但它意味着项目这个阶段还在整理来源,不是最终形态。第二,Fesod 会打包 POI。README 里专门警告过:如果你的项目本身已经引入了 POI,引入 Fesod 时必须手动排除 POI 相关的 jar,否则会撞版本冲突。
这里说的孵化是 Apache 的一个正式阶段。项目进孵化器之后,要按 ASF 的治理、法务和发布流程走一遍,通过评审才能毕业成顶级项目。孵化期的项目可以用,但发布节奏和治理成熟度都还在路上。IP 清理处理的是代码来源,每一段从别处搬来的代码,版权归属和许可证头都得交代清楚,Apache 对这一块卡得严,所以这个阶段会拖上一阵。
迁移路径还有一点要注意。官方给的迁移指南是 FastExcel 迁到 Fesod,不是从 EasyExcel 直接迁,中间为 FastExcel 保留了 FastExcel、FastExcelFactory 这类已废弃的别名作为过渡。FastExcel 本来就是 EasyExcel 的延续,入口类和方法签名都没大改,从它再往 Fesod 走改动面最小。EasyExcel 用户直接跳到 Fesod,等于要一次性处理两个阶段的差异,所以实际上是要跨两层。

四、三者的坐标、包名和入口类
这一节是三者的直接对照,放一张表:
| 维度 | EasyExcel | FastExcel | Apache Fesod |
|---|---|---|---|
| 坐标 | com.alibaba:easyexcel:4.0.3 |
cn.idev.excel:fastexcel:1.3.0 |
org.apache.fesod:fesod-sheet:2.0.2-incubating |
| 根包名 | com.alibaba.excel |
cn.idev.excel |
org.apache.fesod.sheet |
| 入口类 | EasyExcel |
FastExcel |
FesodSheet |
| 协议 | Apache-2.0 | Apache-2.0 | Apache-2.0 |
| 最新版本 | 4.0.3(2024-09) | 1.3.0(2025-08) | 2.0.2-incubating(2026-05-30) |
| 维护状态 | 已归档(2025-09-04),只读 | 活跃,已捐给 ASF | Apache 孵化中 |
| 迁移成本 | 不适用(已归档) | 低(API 兼容,改包名 + 入口类) | 中到高(跨两层 + 扩展点需重写验证) |
三个库的底层都是 POI,只是封装层次不同。EasyExcel 4.0.3 用的是 POI 5.2.5,Fesod 用的是 POI 5.5.1。这个 POI 版本差不是小事,后面讲扩展点的时候会回到这里:一旦你的代码摸到 POI 那一层,这个版本差就意味着要重新验证。
对照的时候别只盯着版本号新旧。引一个 Excel 库,实际上把 POI 和它那一串传递依赖也引了进来,所以换库时真正决定成本的是两件事:你的代码摸到了哪一层,以及底层引擎换了多少。只用到基础读写,三个库换起来都轻松;摸到 handler 或者直接调 POI,成本就取决于底层 POI 版本差了多少。EasyExcel 到 Fesod,这一跳是从 POI 5.2.5 到 5.5.1。

五、真要迁的话,改哪些地方
动手之前,先把自己代码里的使用面摸清楚。我的做法是搜 import,把 com.alibaba.excel 和 org.apache.poi 这两个前缀都过一遍,再看有没有类实现了 handler 接口。这一步花不了多少时间,但它能直接告诉你这次迁移是机械替换,还是要重写加验证。摸完之后再动 POM。
下面按依赖、代码、扩展点三层来拆,能改的和要验的分开看。
先说依赖。假设你原来在根 POM 里统一管 EasyExcel 4.0.3,迁移时先换坐标,注意 artifact 是 fesod-sheet:
1 | <!-- 迁移前:EasyExcel 4.0.3 --> |
用 fesod-bom 统一管版本会省事一点,但排除 POI 这件事不能省。
import 和入口类的替换是机械的。三个库的根包名和入口类一一对应:
1 | // EasyExcel |
@ExcelProperty 注解的简单名在三者之间是一致的,所以 DTO 上的注解通常不用动,改 import 就行。
下面是一个迁到 Fesod 之后的导出示例,写法上刻意和 EasyExcel 保持一致,方便对照:
1 | import org.apache.fesod.sheet.FesodSheet; |
导入侧也一样,基本是机械替换:
1 | List<OrderImportDTO> rows = FesodSheet.read(inputStream) |
读取这一侧,doReadSync 会一次性返回解析好的对象列表,适合数据量可控的导入;数据量大的话,仍然建议用监听器逐行处理,别把整张表读进内存再落库。迁到 Fesod 之后,这套读法没有变化,改的还是 import 和入口类。
到这里看着挺轻松,但「换包名就行」这句话只对基础读写成立,真正的工作量在扩展点上。
另外,import 不一定都摆在明面上。有些项目会把 EasyExcel 的类型藏在泛型或者反射里,grep 看不出来,这种情况只能靠编译器和测试兜底。
一旦你用到写处理器这类扩展点,或者代码里直接 import 了 org.apache.poi.*,迁移就不是改包名那么简单了。常见的几类:
| 扩展点 | 迁移时需要做什么 |
|---|---|
CellWriteHandler |
逐个 handler 重新验证回调时机与 API 签名 |
SheetWriteHandler |
重新验证 sheet 生命周期回调 |
AbstractRowHeightStyleStrategy |
自定义行高策略需重新验证 |
OnceAbsoluteMergeStrategy |
合并策略需重新验证 |
WriteCellData + ImageData |
图片嵌入链路需重新验证 |
直接使用 org.apache.poi.*(XSSFSheet、PrintSetup、CellRangeAddress) |
POI 从 5.2.5 升到 5.5.1,需按新版本重新验证 |
最后一行最容易被忽略。只要代码里直接 import 了 org.apache.poi.*,POI 从 5.2.5 到 5.5.1 这段迁移就躲不掉,得按新版本重新验证。EasyExcel 的扩展点很多时候就是把 POI 对象直接暴露给你,所以在真实项目里,「用 EasyExcel 的 handler」和「直接调 POI」往往是混在一起的。
表里没有一项是难改的,难的是验证。改 import 是几分钟的事,但要让每个 handler 在新的 POI 版本上行为一致,得把导出结果逐项对比。这也是为什么我把这类迁移归到「中到高」成本,而不是「低」。
六、容易搞错的几个点
这几点都不复杂,但被传错的频率很高。
EasyExcel 的最后一个版本是 4.0.3,2024 年 9 月。不是 3.3.2。3.3.2 是 2023 年 5 月的东西,它后面还有 3.3.3、3.3.4,最后才是 4.0.3。4.0.3 这次升级的重点之一就是 JDK 21 支持,所以只看到 3.3.2,会漏掉归档前最关键的一次变化。
协议方面,EasyExcel、FastExcel、Fesod 三个都是 Apache-2.0,没有一个是 MIT。FastExcel 和 Fesod 尤其容易被当成 MIT,这两处都要注意。
入口类叫 FesodSheet,不是 Fesod;调用是 FesodSheet.write,不是 Fesod.write。这个写错代码直接编译不过。坐标是 org.apache.fesod:fesod-sheet,不存在 org.apache.fesod:fesod 这个 artifact。当前版本是 2.0.2-incubating。Fesod 是独立的 Apache 孵化项目,不是 POI 的子模块,它只是用 POI 当引擎。
这些其实都不需要靠记忆。坐标去 Maven Central 搜,入口类和包名看官方文档或源码,协议看仓库根目录的 LICENSE,版本看 release 记录,都是几分钟的事。
还有一点:迁移是两层,不是一次改包名。官方主线是 FastExcel 到 Fesod,EasyExcel 用户得先过 FastExcel 这一层。
最后一个人名容易混。FastExcel 是庄家钜主导的,他原本就是 EasyExcel 的维护者;而 EasyExcel 最初的作者是姬朋飞。这是两个人。把这两者当成同一个人,会让人以为 FastExcel 是原作者的回归之作,其实它是维护者在项目停滞后接着往下做的一条 API 兼容的延续线。
这些点单看都是小地方,但选型的时候会被放大:版本号看错,可能让你以为项目早就死了;协议看错,合规评估就建在错误前提上;坐标和入口类看错,代码连编译都过不了。
七、我们自己的项目,迁还是不迁
背景得先交代,否则结论没意义。我们的技术栈是 Spring Boot 3.2 加 JDK 21,多模块 Maven 单体,Excel 用的是 EasyExcel 4.0.3,版本在根 POM 里统一管。
真正碰 Excel 的代码不多:4 个 Java 文件,大约 600 行,集中在 2 个应用里。除此之外整个代码库没有别的地方碰 Excel。这个规模不大,但也不是随手就能推平。代码量小意味着盘点快,难度集中意味着真正要花时间的就那一个文件。
这 4 个文件里难度分布很不均。有一个大概 400 行的样式导出器是硬骨头:它嵌了 logo 图片,做了单元格合并,设了自定义行高,逐单元格配了样式,还配了 A4 打印,页脚页码加重复表头行。它同时用了 EasyExcel 的 handler 扩展点和原生 POI 类。剩下的很轻,一个模板下载,加一个带单个 @ExcelProperty DTO 的 doReadSync 导入,基本是机械活。
我们最后决定不迁,理由三条:
- 4.0.3 已经在 JDK 21 / POI 5.2.5 上正常工作,没有影响我们的 CVE;
- 归档的库不等于有漏洞的库,它只是不再有新功能,而我们不需要新功能;
- Fesod 还在孵化期,IP 清理尚在进行,而那个 400 行的样式导出器迁过去等于重写加重新验证,功能上没有任何收益。
这三条里,第三条是关键。前两条是防守,第三条是实打实的成本。那个导出器里,图片、合并、行高、样式、打印设置和 POI 原生调用是混在一起的,换底层引擎意味着这些行为都要重新验一遍。验证这种代码,靠跑一遍导出看一眼是不够的,得逐项对照格式和打印效果。
真要迁,工作量大致是这样:把 handler 和 POI 调用逐个对照新版本改一遍,再拿真实数据跑导出,逐项检查图片位置、合并范围、行高、单元格样式和打印分页。这些检查没有捷径,因为 POI 版本变了,同样的调用行为可能有细微差别。
说到底,迁移在我们这里的投入产出比是负的:付出一个复杂导出器的重写成本,换来的是一个名字更正统的依赖。
那什么时候才值得迁?我们给自己列了触发条件,满足任意一条再动手:
- 有真正可利用的漏洞落到 EasyExcel 4.0.3 上;
- 确实需要 Fesod 独有的能力,比如 Excel 转 PDF、读取指定行区间这类 EasyExcel 做不到的功能;
- 团队统一要求依赖必须挂在 Apache 基金会名下,合规或采购层面的硬性要求。
在这三条之外,为了追热度去迁一个已经稳定的导出模块,不划算。
除此之外我们没做别的动作。EasyExcel 4.0.3 继续用,版本锁在根 POM 里,依赖扫描照常跑。哪天扫描真的报出一个可利用的漏洞,再按第一条触发迁移,那时候该评估什么、改哪里,也已经清楚了。
八、写在最后
这轮变化里最容易被混在一起的是两件事:毕业和停更。
EasyExcel 是停更,代码冻结,不再有新功能,但现有版本仍然能用。FastExcel 和 Fesod 是毕业,项目从个人维护走向基金会治理,获得了更长期的生存保障。
这两件事方向完全不同,但对使用者的意义经常被搅在一起。一个稳定的依赖停更,对绝大多数项目不构成风险;一个正在孵化的项目毕业,也不代表你必须跟过去。选型是把你的风险和收益对上,不是谁新就跟谁。如果你的 Excel 需求已经稳定,底层库又没有实际风险,那不动往往就是最省事也最稳的选择。
能用不等于该换。
先把自己项目里的 Excel 代码盘清楚,再决定要不要动。多数时候你会发现,真正要改的就是那个最复杂的文件,而它恰恰是最不该为了「新」而重写的。