如何解决JavaFX项目中commons.math3与SparseBitSet模块未找到错误?
问题背景
开发JavaFX读取Excel的程序,已在Gradle中添加POI 5.2.2依赖,但运行时抛出模块找不到错误:
> Task :Main.main() FAILED Error occurred during initialization of boot layer java.lang.module.FindException: Module commons.math3 not found, required by org.apache.poi.poi
或:
Error occurred during initialization of boot layer java.lang.module.FindException: Module SparseBitSet not found, required by org.apache.poi.poi.ooxml
项目使用JPMS模块化配置(通过org.javamodularity.moduleplugin插件),build.gradle代码如下:
plugins { id 'java' id 'application' id 'org.javamodularity.moduleplugin' version '1.8.12' id 'org.openjfx.javafxplugin' version '0.0.13' id 'org.beryx.jlink' version '2.25.0' } group 'com' version '1.0-SNAPSHOT' repositories { mavenCentral() } ext { junitVersion = '5.9.2' } sourceCompatibility = '17' targetCompatibility = '17' tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } application { mainModule = 'com.al_makkah_traders_app' mainClass = 'com.al_makkah_traders_app.Main' } javafx { version = '17.0.6' modules = ['javafx.controls', 'javafx.fxml'] } dependencies { implementation 'org.controlsfx:controlsfx:11.1.2' implementation 'org.apache.logging.log4j:log4j-core:3.0.0-alpha1' implementation 'org.apache.poi:poi:5.2.2' implementation 'org.apache.poi:poi-ooxml:5.2.2' implementation 'mysql:mysql-connector-java:8.0.33' testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") } test { useJUnitPlatform() } jlink { imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] launcher { name = 'app' } } jlinkZip { group = 'distribution' } jar { duplicatesStrategy = 'EXCLUDE' manifest { attributes( 'Main-Class': 'com.al_makkah_traders_app.Main' ) } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } }
问题原因
POI 5.x的依赖(如commons.math3、SparseBitSet)属于自动模块(没有module-info.java的JAR),而项目启用了JPMS模块化。JPMS要求主模块必须显式声明所有依赖的模块,即使这些依赖已通过Gradle引入,模块系统也不会自动加载未声明的模块。
解决方案
1. 完善module-info.java的依赖声明
在主模块com.al_makkah_traders_app的module-info.java中,添加所有POI及其依赖模块的requires语句:
module com.al_makkah_traders_app { // JavaFX模块 requires javafx.controls; requires javafx.fxml; // POI核心模块 requires org.apache.poi.poi; requires org.apache.poi.poi.ooxml; // POI依赖的自动模块 requires commons.math3; requires com.github.virtuald.sparsebitset; // SparseBitSet的标准模块名 requires org.apache.commons.codec; requires org.apache.commons.collections4; requires org.apache.commons.io; requires org.apache.commons.logging; requires org.apache.xmlbeans; // 其他依赖模块 requires org.controlsfx.controls; requires org.apache.logging.log4j.core; requires mysql.connector.java; // 允许FXML访问主模块包(按需添加) opens com.al_makkah_traders_app to javafx.fxml; exports com.al_makkah_traders_app; }
注:自动模块名称可通过查看JAR的Automatic-Module-Name属性确认,部分依赖的模块名可能随版本略有变化。
2. 验证Gradle模块化配置
org.javamodularity.moduleplugin插件会自动映射Gradle依赖为模块依赖,但仍需在module-info.java中显式声明所有直接/间接依赖的模块,确保模块系统能正确识别。
3. (可选)临时关闭模块化(不推荐)
若暂时不想处理模块化,可移除org.javamodularity.moduleplugin插件并删除module-info.java,将项目改为非模块化模式。但此方式会失去JPMS的优势,且无法使用jlink构建自定义运行时镜像。
验证
修改后重新运行项目,模块找不到的错误应会消失,POI可正常读取Excel文件。
内容的提问来源于stack exchange,提问作者AdnanRafique

