Springboot 项目 org.apache.poi 版本冲突

最近维护一个老系统,需要增加 Excel 导出的功能,普通的导出倒是没什么问题,可是设置表格样式的时候(比如居中),一直报错;百度搜索基本都说是版本太低导致的,升级版本即可。

经过尝试,升级最新版本还是报错,后来继续搜索到说是因为版本冲突导致的,经过仔细检查,确认版本没问题,maven实际引入包也没问题。

后来全局搜索发现,父模块 pom 引入的版本与子模块 pom 本身版本不一致 导致的。

项目环境

Springboot 父子模块架构,在父模块 parent 下,包括 dao、modules、commons、service 以及业务模块,如图:
在这里插入图片描述

父模块(parent)引入 <poi.version>3.13</poi.version>

子模块(gateway)引入 <poi.version>4.1.2</poi.version>

错误原因

由于项目年代久远,维护周期长,参与人数多且杂,技术工具不统一,项目中存在多个 jar 版本冲突问题,而且由于子模块之间相对隔离,冲突问题表现也不明显,导致冲突隐藏比较深。

此次问题的原因是:之前有开发人员将 Excel 导出功能作为工具类写在 commons 模块下,使用版本为 <poi.version>3.13</poi.version>,而 pom 没有在 commons 模块引入,而是在 parent 父模块下引入,这样会导致,所有子模块都会引入此版本。

如果子模块引入其他版本,比如 <poi.version>4.1.2</poi.version> ,此时不会表现出冲突,也不会报错,让人误认为,已经使用高版本(4.1.2) jar 包,实际上,还是使用的父模块的低版本(3.13) jar 包。

以下是错误完整信息:

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.apache.poi.ss.usermodel.CellStyle.setAlignment(S)V
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:982)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
	at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)

问题解决

既然找到问题原因,解决就很简单了,统一版本即可

对比之后,选择统一使用高版本 (4.1.2)

  1. 首先修改 pom 文件,统一版本
<poi.version>4.1.2</poi.version>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>${poi.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>${poi.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>${poi.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>${poi.version}</version>
</dependency>
  1. 修改版本升级代码。jar 包升级之后,一些旧方法被新方法替代。需要找出全部弃用方法,并修改使用新方法。

注意:本项目修改 pom 之后,重新导包,然后执行编译:mvn compile。项目编译报错,根据报错找到需要修改的 poi 相关代码即可。

以下为项目中的修改:

// 行与单元格操作
CellStyle cellStyle = workbook.createCellStyle();
// 单元格设置居中
// 旧版(3.12)
cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
// 新版(4.1.2)
cellStyle.setAlignment(HorizontalAlignment.CENTER);
Cell cell = row.createCell(0);
// 单元格设置类型
// 旧版(3.12)
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
// 新版(4.1.2)
cell.setCellType(CellType.STRING);
// 判断单元格数据类型
// 旧版(3.12)
if (hssFCell.getCellType() == HSSFCell.CELL_TYPE_BOOLEAN) {}
// 新版(4.1.2)
if (hssFCell.getCellType() == CellType.BOOLEAN) {}

依次修改所有报错的地方之后,编译通过,版本统一完成。

在这里插入图片描述

参考博客:

解决org.apache.poi.ss.usermodel.CellStyle.setAlignment(S)V问题
HSSFCellStyle.ALIGN_CENTER HSSFCellStyle.VERTICAL_CENTER 等爆红的解决办法
Apache POI使用详解

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐