Java 17 环境下 EasyPoi 反射访问异常分析与解决方案(ExcelImportUtil.importExcelMore)

问题描述

在 Java 17 环境中使用 EasyPoi 进行 Excel 导入操作时(ExcelImportUtil.importExcelMore),出现以下反射访问异常:

java 复制代码
java.lang.reflect.InaccessibleObjectException: Unable to make field final boolean java.util.LinkedHashMap.accessOrder accessible: module java.base does not "opens java.util" to unnamed module @6da21078
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
	at java.base/java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:130)
	at org.apache.commons.lang3.builder.ReflectionToStringBuilder.appendFieldsIn(ReflectionToStringBuilder.java:649)
	at org.apache.commons.lang3.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:853)
	at org.apache.commons.lang3.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:387)
	at org.apache.commons.lang3.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:156)
	at cn.afterturn.easypoi.excel.imports.ExcelImportService.importExcel(ExcelImportService.java:270)
	at cn.afterturn.easypoi.excel.imports.ExcelImportService.importExcelByIs(ExcelImportService.java:482)
	at cn.afterturn.easypoi.excel.ExcelImportUtil.importExcelMore(ExcelImportUtil.java:99)

原因分析:

  1. Java 模块系统限制

    Java 9 引入了模块系统(JPMS),在 Java 17 中进一步加强了封装性。java.base 模块中的 java.util 包默认不对未命名模块开放反射访问权限。

  2. EasyPoi 内部实现

    EasyPoi 在处理 Excel 导入时,内部使用了 Apache Commons Lang 的 ReflectionToStringBuilder 进行反射操作,试图访问 LinkedHashMap.accessOrder 字段。

  3. 依赖版本兼容性

    我这边使用的是新版本的 EasyPoi (4.5.0) 和 Commons Lang3 (3.17.0),在某些 Java 17+ 环境中仍然会遇到此问题。


解决方案:

方案一:添加 JVM 参数

在启动命令中添加,详细可看官网对此描述官网

bash 复制代码
java --add-opens java.management/sun.management=ALL-UNNAMED

方案二:使用其他方式实现,例如Apache POI、Easyexcel等

java 复制代码
public void import(MultipartFile vettingFile) {
    // 创建 DataFormatter 用于格式化单元格值
    DataFormatter dataFormatter = new DataFormatter();
    InputStream inputStream = null;
    Workbook workbook = null;
    List<Map<String, Object>> resultList = new ArrayList<>();
    
    try (InputStream inputStream= vettingFile.getInputStream()) {
        workbook = new XSSFWorkbook(inputStream);

        // 解析 Excel 文件
        Sheet sheet = workbook.getSheetAt(0);
       
        // 读取标题行(第5行,索引4)
        Row headerRow = sheet.getRow(4);
        if (headerRow == null) {
            return Result.succeed("analyzing-import-file-failed");
        }
        
        // 构建列映射
        Map<Integer, String> columnMapping = new HashMap<>();
        for (int i = 0; i < headerRow.getLastCellNum(); i++) {
            Cell cell = headerRow.getCell(i);
            if (cell != null) {
                // 清理列名:去除换行符和前后空格
                String columnName = dataFormatter.formatCellValue(cell)
                        .replace("\n", " ")
                        .replace("\r", "")
                        .trim();
                if (!columnName.isEmpty()) {
                    columnMapping.put(i, columnName);
                }
            }
        }

        // 读取数据行(从第6行开始,索引5)
        for (int i = 5; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) continue;
            
            Map<String, Object> rowData = new HashMap<>();
            boolean hasData = false;
            
            for (Map.Entry<Integer, String> entry : columnMapping.entrySet()) {
                int colIndex = entry.getKey();
                String columnName = entry.getValue();
                Cell cell = row.getCell(colIndex);
                
                // 清理单元格值
                String cellValue = dataFormatter.formatCellValue(cell)
                        .replace("\n", " ")
                        .replace("\r", "")
                        .trim();
                
                if (!cellValue.isEmpty()) {
                    hasData = true;
                }
                rowData.put(columnName, cellValue);
            }
            
            if (hasData) {
                resultList.add(rowData);
            }
        }
    } catch (Exception ex) {
        log.error("Analyze import file exception: ", ex);
    } finally {
        // 关闭资源
        closeResources(workbook, inputStream, poifs);
    }
}

// 资源关闭辅助方法
private void closeResources(Workbook workbook, InputStream inputStream) {
    try {
        if (workbook != null) {
            workbook.close();
        }
        if (inputStream != null) {
            inputStream.close();
        }
    } catch (Exception ex) {
        log.error("Close resources exception: ", ex);
    }

}

相关推荐
2601_962299241 小时前
Azure python操作系统列表
python·操作系统·azure·虚拟机·存储配置文件
2601_962071571 小时前
【Java报错已解决】org.springframework.beans.factory.BeanCreationException
java·开发语言
zww89491112 小时前
酒馆预约系统开发实战:从需求分析到上线全流程指南
java·eclipse
学术 学术 Fun2 小时前
如何用 API 与 Webhook 批量把图表图片转换成 VSDX
python·自动化·api·visio
淡海水2 小时前
07-04-并发-ConcurrentBag-T-工作窃取WorkStealing算法
开发语言·算法·c#·bag·concurrent·workstealing
2601_962100543 小时前
python包和模块的内容整理
python·模块·导入·虚拟环境·
CS_Zero3 小时前
C语言sizeof是函数吗?
c语言·开发语言
二进制漫游记3 小时前
FastAPI项目集成 Qdrant向量数据库+阿里云Embedding完整实战(工具封装+业务调用)
数据库·python·阿里云·embedding
-今昭-4 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
2601_962381584 小时前
【转】Python渗透测试工具:sqlmap
python·渗透测试·sql注入·sqlmap·数据库安全