如何在 Java 中创建独立的 .exe(无需安装程序和 JRE 即可运行)

How to create a standalone .exe in Java (that runs without an installer and a JRE)

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

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

为此你必须生成一个 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 -> 运行 目标 - > 其他目标 -> 商店包

  3. 您将在存储文件夹中的项目中找到您的 fat jar。

  4. 打开launch4j

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

  6. 指定你的 fat jar 在哪里

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

  8. 单击构建包装器

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

解决方案

使用warp-packer to create a exe out of the image and app launcher created by jlink.

第一个:

  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 构建工具插件等

相关回答

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

  • How do I bundle a JRE into an EXE for a Java Application?

教程

如果需要更多信息,请参阅完整的教程示例。

解决方案说明

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

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

关键是“OH GOD SPIDERS”评论中的建议,使用"warp" tool for packaging, in conjunction with other comments suggesting interfacing with jlink

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

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

限制

  • 根据设计,这是一个仅 Windows 的构建解决方案,它必须 运行 在 Windows machine 上,两者都在构建时时间和部署时间以及 运行 时间。

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

高级步骤

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

    • 应用程序必须没有自动依赖项(应用程序本身和它所依赖的所有传递依赖项必须定义为 Java 具有正确定义的模块-info.java 文件的模块)。
  2. Link 使用启动脚本创建 运行time 图像的应用程序。

  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 machines 的朋友。
  8. 朋友们将能够运行从命令行或双击 exe 文件。

不需要安装您的应用程序,不需要提取或解压缩任何档案,不需要 Java 运行 时间安装,也不需要其他额外的安装。所需要做的就是将 exe 文件复制到 windows machine(例如,单击网络上的下载 link 或从邮件附件中复制 exe),然后双击单击 exe 立即 运行 您的应用程序。

文件树

C:\dev\hellowarp>tree /a /f .
Folder PATH listing for volume Local Disk
Volume serial number is 00000086 C034:A84E
C:\DEV\HELLOWARP
|   pom.xml
|
+---src
|   \---main
|       +---java
|       |   |   module-info.java
|       |   |
|       |   \---com
|       |       \---example
|       |           \---hellowarp
|       |                   HelloController.java
|       |                   HelloWarp.java
|       |
|       \---resources
|           \---com
|               \---example
|                   \---hellowarp
|                           hello-view.fxml
|
\---tools
        warp-packer.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>

模块-info.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

Java外汇申请。

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 控制器class.

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!");
    }
}

你好-view.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>

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

常见问题解答

FAQ 部分仅提供上下文信息。如果您已经知道此信息,或者不需要它,您可以忽略此部分。

Is this an alternative distribution method to a "fat jar"?

是的,我也这么认为。

What would this be appropriate for?

一个小型应用程序,分布在您知道用户 运行宁 Windows.

的环境中

Can I also package my app as an MSI installer?

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

Would it be better to use a "git repo", "fat jar", a exe, a packaged installer or "zipped" image?

这取决于你在做什么。

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

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

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

或者,如果只是 windows,这种打包为“exe”分发的解决方案可能会很有效。

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

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

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

Can I use the Apache jlink Maven plugin instead of the OpenJFX Maven plugin.

此时 OpenJFX JavaFX Maven plugin or the akman 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 拒绝 link 他们。我找不到解决该问题的方法。

When I double click on the exe, there is blank window with the name of the exe in the title bar in addition to my application window.

是的。根据应用程序的不同,这可能是一个小麻烦,也可能是一个阻碍。

双击黑屏的显示正是此处介绍的此解决方案的工作方式。

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

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

Can I use this technique to create single file executables for other operating systems?

是的,我相信是这样。

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

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

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

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

What is the size of the generated executable?

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

What is the startup time?

我没有测量,但是启动时间(双击exe后显示应用程序GUI window的时间)好像是一秒左右。

Could I create an exe for a non-modular Java project.

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