【问题标题】:How to create a standalone .exe in Java (that runs without an installer and a JRE)如何在 Java 中创建独立的 .exe(无需安装程序和 JRE 即可运行)
【发布时间】:2021-12-17 00:55:53
【问题描述】:

如何在没有程序安装程序的情况下为我的JavaFX 项目(我在 IntelliJ IDEA 中编写)制作独立的 Windows 可执行文件(.exe)?我希望用户下载 .exe 文件并立即运行它,无需安装程序,即使他们的计算机上没有 JRE。这甚至可能吗?到目前为止,我已经阅读了一些关于以下选项的内容:

  • launch4j - 似乎将所需的 JRE 文件 复制到 .exe
  • install4j - 它创建了一个安装程序,一旦运行,就会创建.exe 文件。

所以,如果我理解正确的话,这两个都不会帮助我。我找到了thisthis 的帖子,但它们没有涵盖我的具体情况。 对不起,如果我问的是一个愚蠢的问题,这是我第一次使用 Java GUI。

【问题讨论】:

  • 如果可行,您可以尝试使用 graalvm/native-image。
  • java 应用程序仅适用于 jre。要实现您想要的,您需要将jre 与应用程序本身一起分发(jlink 会帮助您)。然后,您制作一个本机启动器 (exe) 来使用您随其分发的 jre 启动您的应用程序。
  • @Boaz 如果我正确理解了答案,JCreator 会生成 .jar 可执行文件,而不是 .exe。但我的可执行文件需要是一个exe。这个和非安装程序、非 JRE 要求是任务的一部分。我知道他们有多愚蠢,但我无法摆脱他们。
  • 您可以将 JRE 与您的程序捆绑在一起,这样用户就不需要已经安装了它(Launch4j 应该能够做到这一点)。然而,运行一个没有任何 JRE 的 java 程序需要您以某种方式将其编译为本机代码。为此有一些编译器(请参阅:stackoverflow.com/questions/2991799/…),但老实说,我不敢打赌它们工作得很好。 Java 并非旨在生成无需 JRE 即可运行的代码。
  • @OHGODSPIDERS 当我尝试launch4j时,它在.exe旁边添加了必要的文件,而不是在其中。我是否错过了将 INSIDE 嵌入 exe 的选项,或者它应该是这样的,没有其他方法?

标签: java javafx exe


【解决方案1】:

解决方案

使用warp-packerjlink 创建的图像和应用启动器中创建一个exe

第一:

  1. download link 复制 warp-packer。
  2. 使用 jlink 为您的应用程序生成图像。

然后,创建单个文件应用程序可执行文件。这可以通过一个命令来完成(在一行上运行它并使用您的值而不是 %% 变量):

 %WARP_DIR%\warp-packer 
   --arch windows-x64 
   --input_dir %APP_JLINK_IMAGE_DIR% 
   --exec %APP_JLINK_LAUNCHER_BAT_FILE% 
   --output %APP_SINGLE_EXECUTABLE_FILE_NAME%

该命令可以从命令行手动运行,也可以通过适当的构建工具插件自动运行。

可以使用最适合您的构建环境的方式调用 jlink;例如任何一种:maven 插件、gradle 插件、命令行实用程序、jpackage 实用程序或 jpackage 构建工具插件等。

相关答案

这个想法不是我的,是在这里提出的:

教程

如果需要更多信息,以下是完整的教程示例。

解决方案说明

这个答案很长,因为它试图提供一个完整的例子和额外的上下文建议。它可以更简洁。它的风格更接近于教程或博客文章风格的帖子,而不是 StackOverflow 的答案。希望长度不会令人生畏,并且很容易复制结果。

我对此很好奇,所以我想我会尝试一下。令我惊讶的是,我能够让它工作。所以我在这里记录了如何复制它。

关键是“OH GOD SPIDERS”在 cmets 中的建议,使用"warp" 工具进行打包,并结合其他建议与jlink 接口的 cmets。

我尝试尽可能多地使用Maven 构建工具,所以这个解决方案就是面向这个的。如果您愿意,可以将该解决方案调整到另一个工具链。

解决方案示例使用 FXML 构建 JavaFX 应用程序。如果不包含 FXML,该示例可能会更简单、更小,但我认为展示资源如何与此解决方案一起使用很重要,这就是包含 FXML 的原因。

限制

  • 根据设计,这是一个仅限 Windows 的构建解决方案,必须在构建时以及部署和运行时在 Windows 机器上运行。

  • 它仅适用于依赖于具有 module-info.java 定义的 Java 模块的应用程序。例如,它不适用于 Spring 或 SpringBoot 应用程序,除非它是完全模块化的。直到发布具有完整 Java 平台模块支持的 Spring 6 or SpringBoot 3

高级步骤

  1. 将应用程序构建为Java platform modular application

    • 应用程序必须没有自动依赖项(应用程序本身及其依赖的所有传递依赖项都必须定义为具有正确定义的 module-info.java 文件的 Java 模块)。
  2. 链接应用程序以使用启动脚本创建运行时映像。

  3. 将带有启动脚本的运行时映像转换为 exe。

程序

  1. Install JDK 17(不需要包含 JavaFX 的版本)。
  2. Install Maven
  3. 创建如下所示的项目文件。
  4. Install warptools\warp-packer.exe
  5. 构建并打包项目 (mvn package)。
  6. 运行应用程序 exe (target/hellowarp.exe) 进行测试。
  7. 将应用程序 exe 提供给使用 Windows 机器的朋友。
  8. 朋友们将能够从命令行或通过双击该 exe 来运行该 exe。

无需安装您的应用程序,无需提取或解压缩任何档案,无需安装 Java 运行时,也无需其他额外安装。只需将 exe 文件复制到 Windows 机器上(例如单击网络上的下载链接或从邮件附件中复制 exe),然后双击该 exe 即可立即运行您的应用程序。

文件树

C:\dev\hellowarp>tree /a /f 。 卷本地磁盘的文件夹路径列表 卷序列号为 00000086 C034:A84E C:\DEV\HELLOWARP | pom.xml | +---src | \ - -主要的 | +---java | | |模块信息.java | | | | | \---com | | \ - -例子 | | \---hellowarp | | HelloController.java | | HelloWarp.java | | | \ - -资源 | \---com | \ - -例子 | \---hellowarp |你好-view.fxml | \ - -工具 翘曲包装器.exe

获取和安装warp

在项目的根目录中创建一个新目录\tools

从以下位置下载 Warp:

并将 warp 可执行文件(重命名)复制到以下位置:

\tools\warp-packer.exe

文件

pom.xml

Maven 项目。

<?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>hellowarp</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>17</java.version>
        <javafx.version>17.0.1</javafx.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--compile-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
             <!--create linked image-->
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>jlink</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.example.hellowarp/com.example.hellowarp.HelloWarp</mainClass>
                    <compress>2</compress>
                    <noManPages>true</noManPages>
                    <noHeaderFiles>true</noHeaderFiles>
                    <stripDebug>true</stripDebug>
                    <launcher>${project.artifactId}</launcher>
                </configuration>
            </plugin>
            <!--package image as an exe-->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- obtain warp-packer.exe from: "https://github.com/dgiagio/warp/releases/download/v0.3.0/windows-x64.warp-packer.exe" -->
                    <executable>${project.basedir}\tools\warp-packer.exe</executable>
                    <arguments>
                        <argument>--arch</argument>
                        <argument>windows-x64</argument>

                        <argument>--input_dir</argument>
                        <argument>${project.build.directory}\image</argument>

                        <argument>--exec</argument>
                        <argument>bin\${project.artifactId}.bat</argument>

                        <argument>--output</argument>
                        <argument>${project.build.directory}\${project.artifactId}.exe</argument>
                    </arguments>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

模块信息.java

应用程序的 Java 平台模块定义。

module com.example.hellowarp {
    requires javafx.controls;
    requires javafx.fxml;

    opens com.example.hellowarp to javafx.fxml;
    exports com.example.hellowarp;
}

HelloWarp.java

JavaFX 应用程序。

package com.example.hellowarp;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloWarp extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(
                HelloWarp.class.getResource(
                        "hello-view.fxml"
                )
        );

        Scene scene = new Scene(fxmlLoader.load());

        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

HelloController.java

JavaFX FXML 控制器类。

package com.example.hellowarp;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class HelloController {
    @FXML
    private Label welcomeText;

    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to my JavaFX Application!");
    }
}

HelloView.fxml

UI 视图定义文件。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" prefWidth="250" xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.example.hellowarp.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <Label fx:id="welcomeText"/>
    <Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>

正在运行的hellowarp.exe 应用程序的屏幕截图

常见问题

常见问题部分仅提供上下文信息。如果您已经知道此信息或不需要此信息,则可以忽略此部分。

这是“胖罐”的替代分发方法吗?

是的,我想是的。

这适合什么?

在您知道用户正在运行 Windows 的环境中分发的小型应用程序。

我还可以将我的应用程序打包为 MSI 安装程序吗?

是的。我使用akman jpackage-maven-plugin 做到了这一点,效果很好。为了限制大小和范围,我不会在这个答案中记录。

使用“git repo”、“fat jar”、exe、打包的安装程序或“压缩”图像会更好吗?

这取决于你在做什么。

如果您的目标是其他开发人员,请在 github 上设置一个帐户,将您的项目放在那里,并为开发人员提供一个 maven 或 gradle 构建文件,以便在他们的环境中从源代码构建应用程序。只需将应用程序打包为标准 jar 文件(没有胖 jar)就可以了。您创建的任何 jar 都可以部署到 maven Central。为 jar 使用 module-info.java,以便它可以通过 jlink 链接到打包的应用程序中。

如果是学校项目。这取决于学校的录取偏好。也许他们只是想要 git 中的代码源,而这就是您所需要的。或者,您可以创建一个您提供的(精简)jar 文件(或带有 jar 及其依赖项的 zip),因为标准学校系统上已经安装了所有相关的 Java/JavaFX 软件。

或者它可能是一个已知的操作系统环境,例如Windows、Mac 或 Linux,您可以使用 jpackage 为其中一个(或两个)环境构建可安装包。

或者,如果它只是 Windows,这种打包为“exe”的解决方案可能会很好地工作。

JavaFX 开发不支持"fat jar" configuration。我不推荐它。您可以(目前)让它工作,有时它可能很方便,但您需要决定这样做所涉及的权衡是否值得。

如果您正在构建商业或流行的开源桌面产品,请使用适合您目标平台(例如 msi 或Windows 的 exe,Linux 的 rpm 和 deb,mac 的 dmg)。您可以选择将这些打包格式部署到 Windows 或 Mac 应用商店或 Linux yum/apt 存储库。

如果是移动部署,请使用gluon mobile

我可以使用 Apache jlink Maven 插件代替 OpenJFX Maven 插件吗?

此时OpenJFX JavaFX Maven pluginakman jpackage-maven-plugin 将生成正确的图像。

Apache jlink Maven plugin 目前将失败(使用 JavaFX 17.0.1 和 Apache jlink 插件 3.1.0)。

当我尝试使用 Apache jlink Maven 插件时,它被 JavaFX 平台模块定义弄糊涂了。 Apache jlink 插件开始使用奇怪的幻像模块名称,例如 javafx.graphicsEmpty,它被视为自动模块并传递给 jlink,因此 jlink 拒绝链接它们。我找不到解决这个问题的方法。

当我双击exe时,除了我的应用程序窗口外,标题栏中还有一个名为exe的空白窗口。

是的。取决于应用程序,这可能是一个小麻烦或一个表演障碍。

双击时显示黑屏正是此解决方案的工作方式,如此处所述。

可能有一种方法可以规避这种情况,但我没有对此进行大量调查。您可以查看此处提供的信息(其中讨论了在 MS Windows 中隐藏或最小化应用启动器窗口的各种方法),看看它是否对您有帮助:

如果您不是双击应用程序,而是通过在命令控制台中输入 exe 名称来运行应用程序,则不会出现额外的屏幕,因为您正在输入的现有控制台已经存在。

我可以使用这种技术为其他操作系统创建单文件可执行文件吗?

是的,我相信是的。

目前,除了 Windows 可执行文件,我还没有尝试将此解决方案用于其他任何东西。

warp-packer 能够为各种操作系统生成可执行文件。

要为非 Windows 机器打包,您需要为目标操作系统输入适当的 jlink 图像输出到 warp-packer,然后运行适当的 warp-packer 实用程序(我相信在目标操作系统上)生成单个可执行文件以在该目标操作系统上执行。

如果有兴趣,请参阅 warp-packer 和 jlink 文档。

生成的可执行文件的大小是多少?

对于示例应用程序,我生成的应用程序可执行文件大小为 34 MB。

什么是启动时间?

我没有测量它,但启动时间(双击 exe 后显示应用程序 GUI 窗口的时间)似乎大约是一秒。

我可以为非模块化 Java 项目创建一个 exe。

可能是的,但这超出了我准备在这里讨论的范围,而且方法与这里描述的不同。

【讨论】:

  • 你的回答简直太棒了。我真的很感激!
【解决方案2】:

生成 exe 的绝对要求不是正确的做法,但如果必须这样做,那么 https://docs.gluonhq.com/ 是唯一合理的方法。否则,您应该结合 jlink 来查看 jpackage。忘记所有关于肥罐和其他选择的讨论。这不是如今构建 JavaFX 应用程序的方式。

【讨论】:

  • 我不认为主要思想围绕“JavaFX 应用程序现在不是这样构建的”...更多的是让它以最小的麻烦工作。这对于刚开始练习的人非常有帮助。将 Java 项目构建为“全模块化”是一项相当复杂的任务,尤其是使用 JavaFX。找到一些东西来证明“它简单地工作”的原则将使 Java 保持活力。肥罐是完全合理的。
  • JavaFX 并非如此。除非你真的、真的知道自己在做什么,否则胖罐子根本不起作用。
  • 胖罐子是一种反模式。
【解决方案3】:

为此,您必须生成一个 fat jar(如果您的 java 应用程序有依赖项),如果它没有依赖项,请保持原样,然后使用 launch4j。 要创建一个胖罐子,请执行以下操作:

  1. 在你的 build.xml 文件中添加这组代码:

    <property name="store.jar.name" value="Master Data App"/>
    
    <property name="store.dir" value="store"/>
    <property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
    
    <echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>
    
    <delete dir="${store.dir}"/>
    <mkdir dir="${store.dir}"/>
    
    <jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
        <zipgroupfileset dir="dist" includes="*.jar"/>
        <zipgroupfileset dir="dist/lib" includes="*.jar"/>
    
        <manifest>
            <attribute name="Main-Class" value="sheetupdown.SheetUpDown"/>
        </manifest>
    </jar>
    
    <zip destfile="${store.jar}">
        <zipfileset src="${store.dir}/temp_final.jar"
        excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
    </zip>
    
    <delete file="${store.dir}/temp_final.jar"/>
    
  2. 在您的 java IDE 中,右键单击 build.xml -> Run Target -> Other Targets -> package for store

  3. 你会在你的项目的 store 文件夹中找到你的 fat jar。

  4. 打开launch4j

  5. 指定输出文件(以 .exe 结尾)

  6. 指定你的胖罐子在哪里

  7. (根据需要添加其他选项,例如:图标,jre版本...。)-可选-

  8. 点击构建包装器

【讨论】:

  • Fat jars 是运行模块化应用程序的不受支持的配置(请参阅this answer on creating fat jars with JavaFX components)。我认为要使其在推荐的配置中工作,您需要使用 jlink 创建一个包含核心 JDK 和 JavaFX 模块的运行时映像。然后创建一个胖 jar,其中包含您的非模块化应用程序和依赖项,依赖于该自定义 JDK 映像。也许 launch4j 可以把它全部打包成一个exe(我不知道)。
猜你喜欢
  • 2019-06-19
  • 1970-01-01
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 2011-03-11
  • 2019-06-04
  • 2011-01-01
  • 1970-01-01
相关资源
最近更新 更多