jpackage打包的Windows Java应用无法找到Jar包中的类
问题现象
用jpackage打包Java应用后,目录结构和配置文件看似正常,但运行生成的EazyCNC.exe时出现以下错误:
C:\Users\nyholku>eazycnc-build\bundle\EazyCNC\EazyCNC.exe [0.004s][warning][gc] -XX:+PrintGC is deprecated. Will use -Xlog:gc instead. [0.017s][info ][gc] Using G1 Error: Unable to initialize main class eazycnc.Main Caused by: java.lang.NoClassDefFoundError: purejavahidapi/InputReportListener
但直接使用Java命令运行却正常:
C:\Users\nyholku\eazycnc-build\bundle\EazyCNC>java -cp app/* eazycnc.Main
打包后的目录结构
根目录:
19.11.2023 15.49 <DIR> app 19.11.2023 15.49 562 688 EazyCNC.exe 19.11.2023 15.49 <DIR> runtime 1 File(s) 562 688 bytes 4 Dir(s) 417 389 883 392 bytes free
app目录内容:
Directory of C:\Users\nyholku\eazycnc-build\bundle\EazyCNC\app 19.11.2023 15.49 <DIR> . 19.11.2023 15.49 <DIR> .. 19.11.2023 15.49 262 .jpackage.xml 19.11.2023 15.49 385 091 commons-lang3-3.2.1.jar 19.11.2023 15.49 653 EazyCNC.cfg 19.11.2023 15.49 3 421 155 EazyCNC.jar 19.11.2023 15.49 4 928 humble-video-all-0.3.0.jar 19.11.2023 15.49 1 509 440 jna-5.6.0.jar 19.11.2023 15.49 2 735 878 jna-platform-5.6.0.jar 19.11.2023 15.49 11 712 967 jogamp-fat.jar 19.11.2023 15.49 65 966 json-20200518.jar 19.11.2023 15.49 195 713 miglayout-3.7.jar 19.11.2023 15.49 199 631 purejavacomm-1.0.1.x.jar 19.11.2023 15.49 118 138 purejavahidapi-0.0.13.jar 19.11.2023 15.49 127 628 vecmath.jar 13 File(s) 20 477 450 bytes 2 Dir(s) 417 401 921 536 bytes free
生成的配置文件EazyCNC.cfg
[Application] app.mainjar=$APPDIR\EazyCNC.jar app.classpath=$APPDIR\commons-lang3-3.2.1.jar app.classpath=$APPDIR\humble-video-all-0.3.0.jar app.classpath=$APPDIR\jna-5.6.0.jar app.classpath=$APPDIR\jna-platform-5.6.0.jar app.classpath=$APPDIR\jogamp-fat.jar app.classpath=$APPDIR\json-20200518.jar app.classpath=$APPDIR\miglayout-3.7.jar app.classpath=$APPDIR\purejavacomm-1.0.1.x.jar app.classpath=$APPDIR\purejavahidapi-0.0.13.jar app.classpath=$APPDIR\vecmath.jar [JavaOptions] java-options=-Djpackage.app-version=1.0 java-options=-Xmx1024m java-options=-XX:+UseG1GC java-options=-XX:MaxGCPauseMillis=50 java-options=-XX:+PrintGC
Ant构建配置
jpackage相关target:
<target name="jpackage-windows" if="is_windows" depends="init"> <exec executable="${jpackage}" dir="."> <arg value="--win-console" /> <arg value="--type" /> <arg value="app-image" /> <arg value="--verbose" /> <arg value="--dest" /> <arg value="${bundle-dir}/" /> <arg value="--name" /> <arg value="EazyCNC" /> <arg value="--main-jar" /> <arg value="EazyCNC.jar" /> <arg value="--java-options" /> <arg value="-Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+PrintGC" /> <arg value="--resource-dir" /> <arg value="${resource-dir}" /> <arg value="--input" /> <arg value="${jpackage-input-dir}" /> <arg value="--runtime-image" /> <arg value="${java-runtime-dir}" /> <arg value="--temp" /> <arg value="${build-dir}/wix-tempfiles" /> </exec> </target>
部分构建变量:
<property name="app-image-type" value="exe" /> <property name="jdk-loc" value="C:\Program Files\Java\jdk-11" /> <property name="jpackage" value="C:\Program Files\Java\jdk-21\bin\jpackage.exe" /> <property name="jlink" value="C:\Program Files\Java\jdk-11\bin\jlink.exe" /> <property name="targetos" value="windows" /> <property name="pathsep" value=";" /> <property name="resource-dir" value="package-resources/windows" />
Ant输出的其他变量:
[echo] os.name : Windows 10 [echo] jdk-loc : C:\Program Files\Java\jdk-11 [echo] fxant-loc : ${fxant-loc} [echo] basedir : C:\Users\nyholku\eazycnc-project [echo] build-dir : C:\Users\nyholku/eazycnc-build [echo] lib-dir : ./lib [echo] src-dir : ./src [echo] dist-dir : ./downloads [echo] temp-dir : C:\Users\nyholku/eazycnc-build/temp-dir [echo] classes-dir : C:\Users\nyholku/eazycnc-build/classes [echo] eazycnc_version : 2.0.39 [echo] jpackage-input-dir: C:\Users\nyholku/eazycnc-build/jlink-input
问题原因
问题出在EazyCNC.cfg的classpath配置上:Windows下的ini格式配置文件中,重复的键(这里是app.classpath)会被后续条目覆盖,最终生效的classpath只有最后一行的$APPDIR\vecmath.jar,导致其他依赖包(包括purejavahidapi-0.0.13.jar)不在类路径中,从而抛出NoClassDefFoundError。
而直接用java -cp app/*命令时,Windows的命令行shell会将app/*通配符展开为所有jar文件,类路径包含了所有依赖,因此可以正常运行。
另外,使用JDK21的jpackage搭配JDK11的运行时镜像,可能在自动生成classpath配置时出现了格式兼容问题,导致生成了多个重复的app.classpath条目,而非合并为单个分号分隔的条目。
解决方案
方案1:手动修改配置文件
编辑app/EazyCNC.cfg,将所有app.classpath行合并为一行,用分号分隔所有jar路径:
[Application] app.mainjar=$APPDIR\EazyCNC.jar app.classpath=$APPDIR\commons-lang3-3.2.1.jar;$APPDIR\humble-video-all-0.3.0.jar;$APPDIR\jna-5.6.0.jar;$APPDIR\jna-platform-5.6.0.jar;$APPDIR\jogamp-fat.jar;$APPDIR\json-20200518.jar;$APPDIR\miglayout-3.7.jar;$APPDIR\purejavacomm-1.0.1.x.jar;$APPDIR\purejavahidapi-0.0.13.jar;$APPDIR\vecmath.jar
修改后重新运行EazyCNC.exe即可。
方案2:在Ant构建中显式指定classpath
修改Ant的jpackage执行配置,添加--classpath参数,将所有依赖jar用分号拼接后传入,避免依赖jpackage自动生成classpath:
<target name="jpackage-windows" if="is_windows" depends="init"> <exec executable="${jpackage}" dir="."> <arg value="--win-console" /> <arg value="--type" /> <arg value="app-image" /> <arg value="--verbose" /> <arg value="--dest" /> <arg value="${bundle-dir}/" /> <arg value="--name" /> <arg value="EazyCNC" /> <arg value="--main-jar" /> <arg value="EazyCNC.jar" /> <!-- 新增classpath参数 --> <arg value="--classpath" /> <arg value="commons-lang3-3.2.1.jar;humble-video-all-0.3.0.jar;jna-5.6.0.jar;jna-platform-5.6.0.jar;jogamp-fat.jar;json-20200518.jar;miglayout-3.7.jar;purejavacomm-1.0.1.x.jar;purejavahidapi-0.0.13.jar;vecmath.jar" /> <arg value="--java-options" /> <arg value="-Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+PrintGC" /> <arg value="--resource-dir" /> <arg value="${resource-dir}" /> <arg value="--input" /> <arg value="${jpackage-input-dir}" /> <arg value="--runtime-image" /> <arg value="${java-runtime-dir}" /> <arg value="--temp" /> <arg value="${build-dir}/wix-tempfiles" /> </exec> </target>
重新执行Ant构建,生成的配置文件会自动包含正确的合并后classpath。
内容的提问来源于stack exchange,提问作者nyholku

