如何在JavaFX运行时镜像中包含Python脚本并实现执行与打包?
问题描述
我在JavaFX模块化Maven项目中需要包含一个存放Python脚本及依赖二进制文件的目录,脚本会根据传入参数运行。用Maven直接运行时一切正常,但生成runtime image后,执行target目录下bin里的启动.bat时,Python脚本无法执行。目前已将脚本目录放在resources文件夹,通过Java Runtime调用脚本,相关代码、pom.xml和module-info.java如下,求问如何把脚本纳入运行时镜像,让生成的.bat能正常调用脚本,最终用jpackage完成打包。
现有代码示例
pyPath = Paths.get("src/main/resources/script/main.py").toAbsolutePath().toString(); command = "python"+pyPath+args; runtime = Runtime.getRuntime(); process = runtime.exec(command);
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>gui</artifactId> <version>1.0-SNAPSHOT</version> <name>gui</name> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>5.8.2</junit.version> </properties> <dependencies> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>18</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>18</version> </dependency> <dependency> <groupId>org.controlsfx</groupId> <artifactId>controlsfx</artifactId> <version>11.1.1</version> </dependency> <dependency> <groupId>com.dlsc.formsfx</groupId> <artifactId>formsfx-core</artifactId> <version>11.3.2</version> <exclusions> <exclusion> <groupId>org.openjfx</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.kordamp.ikonli</groupId> <artifactId>ikonli-javafx</artifactId> <version>12.3.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.jfoenix</groupId> <artifactId>jfoenix</artifactId> <version>9.0.10</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.panteleyev</groupId> <artifactId>jpackage-maven-plugin</artifactId> <version>1.5.2</version> <configuration> <name>gui</name> <appVersion>1.0.0</appVersion> <vendor>1234</vendor> <destination>target/dist</destination> <module>com.example.gui/com.example.gui.Application</module> <runtimeImage>target/example-gui</runtimeImage> <winDirChooser>true</winDirChooser> <winPerUserInstall>true</winPerUserInstall> <winShortcut>true</winShortcut> <winMenuGroup>Applications</winMenuGroup> <icon>${project.basedir}/main/resources/img/icon.ico</icon> <javaOptions> <option>-Dfile.encoding=UTF-8</option> </javaOptions> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.10.1</version> <configuration> <source>18</source> <target>18</target> </configuration> </plugin> <plugin> <groupId>org.openjfx</groupId> <artifactId>javafx-maven-plugin</artifactId> <version>0.0.8</version> <executions> <execution> <id>default-cli</id> <configuration> <mainClass>com.example.gui/com.example.gui.Application</mainClass> <launcher>gui-launcher</launcher> <jlinkZipName>gui</jlinkZipName> <jlinkImageName>gui</jlinkImageName> <jlinkVerbose>true</jlinkVerbose> <noManPages>true</noManPages> <stripDebug>true</stripDebug> <noHeaderFiles>true</noHeaderFiles> <options> <option>--add-opens</option><option>javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED</option> <option>--add-opens</option><option>javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED</option> <option>--add-opens</option><option>javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED</option> <option>--add-opens</option><option>javafx.base/com.sun.javafx.binding=ALL-UNNAMED</option> <option>--add-opens</option><option>javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED</option> <option>--add-opens</option><option>javafx.base/com.sun.javafx.event=ALL-UNNAMED</option> <option>--add-exports</option><option>javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED</option> <option>--add-exports</option><option>javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED</option> <option>--add-exports</option><option>javafx.base/com.sun.javafx.binding=ALL-UNNAMED</option> <option>--add-exports</option><option>javafx.graphics/com.sun.javafx.stage=ALL-UNNAMED</option> <option>--add-exports</option><option>javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED</option> <option>--add-exports</option><option>javafx.base/com.sun.javafx.event=ALL-UNNAMED</option> </options> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
注:javafx-maven-plugin的额外选项为适配jfoenix包兼容性添加
module-info.java配置
module com.example.gui { requires javafx.controls; requires javafx.fxml; requires org.controlsfx.controls; requires com.dlsc.formsfx; requires org.kordamp.ikonli.javafx; requires com.jfoenix; opens com.example.gui to javafx.fxml; exports com.example.gui; }
解决方案
1. 修正资源文件访问路径
当前代码直接引用开发环境的src/main/resources路径,生成runtime image后该路径不存在。需通过类加载器获取资源的实际运行路径:
// 从模块中获取资源URL URL resourceUrl = getClass().getResource("/script/main.py"); if (resourceUrl == null) { throw new RuntimeException("找不到Python脚本文件"); } // 转换为本地文件路径 Path pyPath = Paths.get(resourceUrl.toURI()); // 正确构造命令(注意python与路径间加空格,参数拼接要合理) String command = String.format("python \"%s\" %s", pyPath.toString(), args); Process process = Runtime.getRuntime().exec(command);
提示:若脚本依赖的二进制文件也在script目录下,脚本内部可使用相对路径访问,或同样通过类加载器获取二进制文件的路径
2. 确保资源被打包到模块Jar
Maven默认会将src/main/resources下的文件打包到Jar根目录,若未自动生效,可手动添加maven-resources-plugin配置:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.3.1</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin>
3. 配置javafx-maven-plugin将资源纳入Runtime Image
jlink默认仅打包模块class文件和依赖,需显式配置资源目录:
修改javafx-maven-plugin的<configuration>,添加<resources>节点:
<plugin> <groupId>org.openjfx</groupId> <artifactId>javafx-maven-plugin</artifactId> <version>0.0.8</version> <executions> <execution> <id>default-cli</id> <configuration> <!-- 原有配置保留 --> <mainClass>com.example.gui/com.example.gui.Application</mainClass> <launcher>gui-launcher</launcher> <jlinkZipName>gui</jlinkZipName> <jlinkImageName>gui</jlinkImageName> <!-- 添加资源配置 --> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <!-- 原有配置保留 --> <jlinkVerbose>true</jlinkVerbose> <noManPages>true</noManPages> <stripDebug>true</stripDebug> <noHeaderFiles>true</noHeaderFiles> <options> <!-- 原有options保留 --> </options> </configuration> </execution> </executions> </plugin>
4. 修正jpackage-maven-plugin配置并确保资源被包含
修正原配置中的路径错误,并添加资源打包规则:
<plugin> <groupId>org.panteleyev</groupId> <artifactId>jpackage-maven-plugin</artifactId> <version>1.5.2</version> <configuration> <!-- 原有配置保留 --> <name>gui</name> <appVersion>1.0.0</appVersion> <vendor>1234</vendor> <destination>target/dist</destination> <module>com.example.gui/com.example.gui.Application</module> <!-- 修正runtimeImage路径,与javafx-maven-plugin的jlinkImageName一致 --> <runtimeImage>target/gui</runtimeImage> <!-- 添加资源打包配置 --> <appResources> <resource> <directory>src/main/resources/script</directory> <targetPath>script</targetPath> </resource> </appResources> <!-- 原有配置保留 --> <winDirChooser>true</winDirChooser> <winPerUserInstall>true</winPerUserInstall> <winShortcut>true</winShortcut> <winMenuGroup>Applications</winMenuGroup> <!-- 修正图标路径 --> <icon>${project.basedir}/src/main/resources/img/icon.ico</icon> <javaOptions> <option>-Dfile.encoding=UTF-8</option> </javaOptions> </configuration> </plugin>
5. 验证步骤
- 运行
mvn clean javafx:jlink生成Runtime Image,检查target/gui/lib/com.example.gui目录下是否包含script文件夹及内部文件 - 执行
target/gui/bin/gui-launcher.bat测试Python脚本是否正常运行 - 运行
mvn jpackage:jpackage生成安装包,安装后再次验证脚本执行情况
内容的提问来源于stack exchange,提问作者vukca
相关产品推荐
相关产品推荐

