如何在 Java 中创建独立的 .exe(无需安装程序和 JRE 即可运行) [英] How to create a standalone .exe in Java (that runs without an installer and a JRE)

查看:37
本文介绍了如何在 Java 中创建独立的 .exe(无需安装程序和 JRE 即可运行)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

所以,如果我理解正确,这两个都不会帮助我.我发现

常见问题

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

<块引用>

这是fat jar"的替代分发方法吗?

是的,我想是的.

<块引用>

这适合什么?

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

<块引用>

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

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

<块引用>

使用git repo"、fat jar"、exe、打包安装程序或zipped"文件会更好吗?图片?

这取决于你在做什么.

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

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

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

或者,如果只是windows,这个打包为exe"的解决方案用于分发可能效果很好.

A 胖罐子"JavaFX 开发不支持配置.我不推荐它.您可以让它工作(目前),有时也很方便,但您需要决定是否值得为此进行权衡.

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

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

<块引用>

我可以使用 Apache jlink Maven 插件而不是 OpenJFX Maven 插件吗.

OpenJFX JavaFX Maven 插件akman jpackage-maven-plugin 这时候会生成正确的图片.

Apache jlink Maven 插件 目前将失败(与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.

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

How can I make a standalone Windows executable(.exe) for my JavaFX project(I wrote in IntelliJ IDEA), without an installer for my program? I want the user to download the .exe file and run it out of the box, without an installer, even if they don't have a JRE on their computer. Is this even possible? So far I've read a little about the following options:

  • launch4j - It seems to copy the required JRE files alongside the .exe
  • install4j - It makes an installer, which, once runned, creates the .exe file.

So, if I have understood correctly, neither of these two will help me. I found this and this posts, but they don't cover my specific case. Excuse me, if I'm asking a stupid question, this is my first ever Java GUI.

解决方案

Solution

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

First:

  1. Copy warp-packer from this download link.
  2. Generate an image for your application using jlink.

Then, create a single file application executable. This can be done with one command (running it on a single line and using your values instead of the %% variables):

 %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%

The command could be run from the command line manually or automatically via an appropriate build tool plugin.

jlink can be invoked using whichever means works best for your build environment; e.g. any one of: maven plugin, gradle plugin, command line utility, jpackage utility or jpackage build tool plugin, etc.

Related Answer

This idea is not mine, it was proposed here:

Tutorial

A full tutorial example follows if more information is required.

Solution Description

This answer is long because it seeks to provide a complete example with additional contextual advice. It could be more concise. It's style is closer to a tutorial or blog post style post than to a StackOverflow answer. Hopefully the length is not intimidating and it is easy to replicate the outcome.

I was curious about this so I thought I would try it. Surprisingly to me, I was able to get it to work. So I have documented how to replicate that here.

The key is the suggestion in comments by "OH GOD SPIDERS", to use the the "warp" tool for packaging, in conjunction with other comments suggesting interfacing with jlink.

I tried to do as much as possible using the Maven build tool, so this solution is oriented towards that. You could adapt the solution to another tool chain if you prefer.

The solution example builds a JavaFX application with FXML. The example could be simpler and much smaller if it did not include FXML, but I thought it was important to show how resources work with this solution, which is why FXML is included.

Limitations

  • This is, by design, a Windows only build solution which must be run on a Windows machine, both at build time and at deployment and run time.

  • It will only work for applications that rely on Java modules that have module-info.java definitions. For example, it won't work for a Spring or SpringBoot application unless it is fully modular. This is, until Spring 6 or SpringBoot 3 with full Java Platform module support is released.

High Level Steps

  1. Build the application as a Java platform modular application.

    • The application MUST have no automatic dependencies (the application itself and all transitive dependencies it relies on must be defined as Java modules with properly defined module-info.java files).
  2. Link the application to create a runtime image with a launch script.

  3. Turn the runtime image with the launch script into an exe.

Procedure

  1. Install JDK 17 (does not require a version which includes JavaFX).
  2. Install Maven.
  3. Create the project files shown below.
  4. Install warp to toolswarp-packer.exe.
  5. Build and package the project (mvn package).
  6. Run the application exe (target/hellowarp.exe) to test it.
  7. Give the application exe to friends with Windows machines.
  8. Friends will be able to run the exe from the command line or via a double-click on the exe.

No installation of your application is required, no extraction or unzipping of any archives, no Java runtime installation and no other additional installations are required. All that is needed is to copy the exe file to a windows machine (e.g. click on a download link on the web or copy the exe out of a mail attachment), then double click on the exe it to instantly run your application.

File tree

C:devhellowarp>tree /a /f .
Folder PATH listing for volume Local Disk
Volume serial number is 00000086 C034:A84E
C:DEVHELLOWARP
|   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

Obtaining and installing warp

Create a new directory, ools, in the root of your project.

Download Warp from:

And copy the warp executable (renaming it) to the following location:

	oolswarp-packer.exe

Files

pom.xml

Maven project.

<?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}	oolswarp-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>

module-info.java

Java platform module definition for the application.

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

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

HelloWarp.java

JavaFX application.

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

HelloView.fxml

UI view definition file.

<?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>

Screenshot of the running hellowarp.exe application

FAQ

There FAQ section just provides contextual information. You can ignore this section if you already know this info, or if you don't need it.

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

Yes, I think so.

What would this be appropriate for?

A small-scale application distributed in an environment where you know the users are running Windows.

Can I also package my app as an MSI installer?

Yes. I did that using the akman jpackage-maven-plugin, it worked well. To limit size and scope I will not document that in this answer.

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

It depends on what you are doing.

If your target is other developers, set up an account on github, put your project there and provide a maven or gradle build file for the developers to build the app from source in their environment. Just packaging the application as a standard jar file (no fat jar), would be fine. Any jar you create could be deployed to maven central. Use a module-info.java for the jar so that it can be linked via jlink into packaged applications.

If it is a school project. It depends on the acceptance preference for the school. Perhaps they just want the code source in git, and that is all you need. Or maybe you can create a (thin) jar file (or a zip with the jar and its dependencies) which you provide, knowing that the standard school systems have all the relevant Java/JavaFX software already installed on them.

Or maybe it is a known OS environment, e.g. Windows, Mac or Linux, and you can use jpackage to build an installable package for one (or two) of those environments.

Or, if it is windows only, this solution of packaging as an "exe" for distribution might work well.

A "fat jar" configuration is not supported for JavaFX development. I do not recommend it. You can get it to work (currently) and it can be convenient sometimes, but you will need to decide if the trade-offs involved to do that are worth it.

If you are building a commercial or popular open source desktop product, create packaged installers, using a tool like jpackage (or one of the commercial or open source alternatives) in the appropriate format for your target platforms (e.g. msi or exe for windows, rpm and deb for linux, dmg for mac). You may choose to deploy those packaged formats to Windows or Mac app stores or Linux yum/apt repositories.

If it is a mobile deployment, use gluon mobile.

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

The OpenJFX JavaFX Maven plugin or the akman jpackage-maven-plugin will produce the correct images at this time.

The Apache jlink Maven plugin will, currently, fail (with JavaFX 17.0.1 and Apache jlink plugin 3.1.0).

When I tried using the Apache jlink Maven plugin, it got confused by the JavaFX platform module definitions. The Apache jlink plugin started working with weird phantom module names like javafx.graphicsEmpty that it treated as automatic modules and passed to jlink, so jlink refused to link them. I could not find a way around that issue.

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.

Yes. Depending on the application that could either be a minor annoyance or a show stopper.

The display of the black screen on double-click is just the way this solution, as presented here, works.

There may be a way to circumvent this, but I haven't done a lot of investigation into it. You could look at the information supplied here (which discusses various ways to hide or minimize app launcher windows in MS Windows) and see if it helps you:

If, instead of double-clicking on the app, you are running the app by typing the exe name in a command console, there is no additional screen as there is already an existing console that you were typing in.

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

Yes, I believe so.

At this time, I have not tried to use this solution for anything but Windows executables.

warp-packer is, capable of generating executables for a variety of OS systems.

To package for a non-windows machine, you would need to input the appropriate jlink image output for the target operating system to warp-packer and then run the appropriate warp-packer utility (I believe on the target OS) to generate the single executable for execution on that target OS.

If interested, see the warp-packer and jlink documentation.

What is the size of the generated executable?

For the sample application, my generated application executable was 34 megabytes in size.

What is the startup time?

I didn't measure it, but time to startup (time to display the application GUI window after double clicking the exe) appeared to be about a second.

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

Yes probably, but that is outside the scope of what I am prepared to discuss here, and the method would be different from that described here.

这篇关于如何在 Java 中创建独立的 .exe(无需安装程序和 JRE 即可运行)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆