简体   繁体   English

如何使用 JRE 部署 JavaFX 11 桌面应用程序

[英]How to deploy a JavaFX 11 Desktop application with a JRE

I have a JavaFX (JDK 8) desktop business application, which uses Java Web Start for deployment.我有一个 JavaFX (JDK 8) 桌面业务应用程序,它使用 Java Web Start 进行部署。 Users have Java 8 installed, and they simply go to the URL (a public URL on my AWS Linux server) and the application downloads / starts (using Web Start).用户安装了 Java 8,他们只需转到 URL(我的 AWS Linux 服务器上的公共 URL)并下载/启动应用程序(使用 Web Start)。 I can easily update the application, too, by deploying new JARs to the server.我也可以通过将新 JAR 部署到服务器来轻松更新应用程序。 Everything works well.一切正常。

However, Oracle has discontinued Web Start with Java 11, and in their “Java Client Roadmap Update” white paper, March 2018, they recommend bundling a JRE with the application (“The notion of an application being distributed separately from a standalone JRE is, therefore, quickly fading.”).但是,Oracle 已停止使用 Java 11 的 Web Start,并且在 2018 年 3 月的“Java 客户端路线图更新”白皮书中,他们建议将 JRE 与应用程序捆绑在一起(“应用程序与独立 JRE 分开分发的概念是,因此,很快就会消失。”)。 I cannot rely on my users to stay at Java 8 for Web Start, and even if they do remain at 8, Oracle requires a license to continue using Java 8 (which I do not have, can be very costly, and I'd prefer anyways to move with the community towards JavaFX 11 and OpenJDK).我不能指望我的用户在 Web Start 上一直使用 Java 8,即使他们仍然使用 Java 8,Oracle 也需要获得许可才能继续使用 Java 8(我没有,这可能非常昂贵,我更喜欢无论如何要与社区一起向 JavaFX 11 和 OpenJDK 迈进)。

I would like to migrate to JavaFX 11. I have followed OpenJFX's “Getting Started with JavaFX11” ( https://openjfx.io/openjfx-docs/ ), using OpenJDK 11.0.1 with Gluon's JavaFX SDK 11.0.1 (on Netbeans 10vc2), and have been able to get the sample applications to run (and appears to me I should be able to port my JavaFX 8 code to JavaFX 11, quite easily).我想迁移到 JavaFX 11。我遵循 OpenJFX 的“JavaFX11 入门”( https://openjfx.io/openjfx-docs/ ),使用 OpenJDK 11.0.1 和 Gluon 的 JavaFX SDK 11.0.1(在 Netbeans 10vc2 ),并且已经能够运行示例应用程序(在我看来,我应该能够很容易地将我的 JavaFX 8 代码移植到 JavaFX 11)。

However, this is where I am stuck for direction.然而,这是我坚持方向的地方。 How do I bundle this with the JRE and deploy this to my end-users (and provide application updates)?我如何将它与 JRE 捆绑并将其部署到我的最终用户(并提供应用程序更新)? Is there an easy way out there (or even a hard way, with some direction/guides)?有没有简单的方法(或者甚至是困难的方法,有一些方向/指南)?

I can spend hundreds of hours writing expressive, rich, and useful desktop business applications in JavaFX 11, but what do I do then?我可以花费数百小时在 JavaFX 11 中编写富有表现力、丰富且有用的桌面业务应用程序,但是我该怎么办呢?

Are the deployment toolkits, like JWrapper, InstallAnywhere, etc, suited to this new era of Java 11? JWrapper、InstallAnywhere 等部署工具包是否适合 Java 11 的新时代? Does Gluon/openjfx.io perhaps have a recommendation or guide I missed? Gluon/openjfx.io 是否有我遗漏的推荐或指南? I cannot seem to find any recommendations or guides from reputable sources, for how us developers, who focus on writing the front-end code, are to deploy the applications.对于我们专注于编写前端代码的开发人员如何部署应用程序,我似乎无法从信誉良好的来源中找到任何建议或指南。

Thank you for any help or guidance.感谢您的任何帮助或指导。

The way it works now is, you convert your program to a module, then “link” it to all the other modules it requires.它现在的工作方式是,将程序转换为模块,然后将其“链接”到它需要的所有其他模块。

The result of this linking process is what's known as an image.这个链接过程的结果就是所谓的图像。 An image is actually a file tree, which includes a bin directory with one or more ready-to-run executables.映像实际上是一个文件树,其中包含一个bin目录,其中包含一个或多个可立即运行的可执行文件。 This tree is what you distribute, typically as a zip or tar.gz.这棵树是您分发的内容,通常是 zip 或 tar.gz。

The steps are:步骤是:

  1. Create a module-info.java创建一个模块信息.java
  2. Compile with a module path instead of a classpath使用模块路径而不是类路径进行编译
  3. Create a jar from classes, as usual像往常一样从类创建一个 jar
  4. Convert jar to a jmod with the JDK's jmod tool使用 JDK 的jmod工具将 jar 转换为jmod
  5. Link that jmod, and the modules on which it depends, into an image将该 jmod 及其所依赖的模块链接到一个图像中

Writing a module descriptor编写模块描述符

The first step is to turn the application into a module.第一步是将应用程序变成一个模块。 Minimally, this requires creating a module-info.java at the top of your source tree (that is, in the empty package).至少,这需要在源代码树的顶部(即在空包中)创建一个module-info.java Every module has a name, which is often the same as a package name but doesn't have to be.每个模块都有一个名称,它通常与包名称相同,但并非必须如此。 So, your module-info.java might look like this:因此,您的 module-info.java 可能如下所示:

module com.mcs75.businessapp {
    exports com.mcs75.desktop.businessapp;

    requires java.logging;
    requires transitive javafx.graphics;
    requires transitive javafx.controls;
}

Building建筑

When you build, you don't specify a classpath at all.构建时,根本不指定类路径。 Instead, you specify a module path.相反,您指定一个模块路径。

A module path is a list of directories, not files.模块路径是目录列表,而不是文件。 Each directory contains, not surprisingly, modules.毫不奇怪,每个目录都包含模块。 The jmods directory of the JDK is included implicitly. JDK 的jmods目录是隐式包含的。 All you need to include is directories containing the non-JDK modules you need.您需要包含的只是包含您需要的非 JDK 模块的目录。 In your case, that at least means Gluon's JavaFX:就您而言,这至少意味着 Gluon 的 JavaFX:

javac -Xlint -g -d build/classes --module-path /opt/gluon-javafx/lib \
    src/java/com/mcs75/desktop/businessapp/*.java

Then you create a jar the usual way:然后您以通常的方式创建一个 jar:

jar -c -f build/mybusinessapp.jar -C build/classes .

A jar file with a module-info.class in it is considered a modular jar.包含 module-info.class 的 jar 文件被认为是模块化 jar。

Making a jmod制作一个 jmod

Creating a jmod is usually a simple process:创建 jmod 通常是一个简单的过程:

mkdir build/modules
jmod create --class-path build/mybusinessapp.jar \
    --main-class com.mcs75.desktop.businessapp.BusinessApplication \
    build/modules/mybusinessapp.jmod

Linking链接

Finally, you use the JDK's jlink command to assemble everything:最后,您使用 JDK 的jlink命令来组装所有内容:

jlink --output build/image \
    --module-path build/modules:/opt/gluon-javafx/lib \
    --add-modules com.mcs75.businessapp \
    --launcher MyBusinessApp=com.mcs75.businessapp

jlink creates a minimal JRE, which contains only the modules you explicitly add (and the modules those explicit modules require). jlink创建了一个最小的 JRE,它只包含您显式添加的模块(以及那些显式模块需要的模块)。 --add-modules is the required option that specifies what to add. --add-modules是指定要添加的内容的必需选项。

As with other JDK tools, --module-path specifies directories containing modules.与其他 JDK 工具一样, --module-path module --module-path指定包含模块的目录。

The --launcher option causes the final image tree to have an additional executable script in its bin directory with the given name (the part before the equals). --launcher选项使最终的映像树在其bin目录中具有一个附加的可执行脚本,该脚本具有给定的名称(等号之前的部分)。 So, MyBusinessApp=com.mcs75.businessapp means “create an executable named MyBusinessApp, which executes the module com.mcs75.businessapp.”因此, MyBusinessApp=com.mcs75.businessapp意思是“创建一个名为 MyBusinessApp 的可执行文件,它执行模块 com.mcs75.businessapp”。

Because the jmod create command included a --main-class option, Java will know what to execute, just like declaring a Main-Class attribute in a manifest.因为jmod create命令包含一个jmod create --main-class选项,Java 将知道要执行什么,就像在清单中声明 Main-Class 属性一样。 It is also possible to explicitly declare a class to execute in the --launcher option if desired.如果需要,还可以在--launcher选项中显式声明要执行的类。

Distributing分发

What you will want to distribute is a zip or tar.gz of the entire image file tree.您将要分发的是整个图像文件树的 zip 或 tar.gz。 The executable that the user should run is in the image's bin directory.用户应该运行的可执行文件位于映像的bin目录中。 Of course, you are free to add your own executables.当然,您可以自由添加自己的可执行文件。 You are also free to place this into any kind of installer, as long as the image tree's structure is preserved.您也可以随意将其放入任何类型的安装程序中,只要保留图像树的结构即可。

A future JDK will have a packaging tool for creating full-fledged native installers.未来的 JDK 将有一个打包工具,用于创建成熟的本机安装程序。 As of Java 14, the JDK has a jpackage tool which can create native installers.从 Java 14 开始,JDK 有一个jpackage工具,可以创建本机安装程序。 For example:例如:

jpackage -n MyBusinessApp --runtime-image build/image \
    -m com.mcs75.businessapp/com.mcs75.desktop.businessapp.BusinessApplication

-n specifies the name of the program. -n指定程序的名称。 --runtime-image specifies the location of the existing jlink'd image. --runtime-image指定现有 jlink 图像的位置。 -m is the module and class within the jlink'd image to execute, much like the portion after the = in jlink's --launcher option. -m是 jlink 映像中要执行的模块和类,很像--launcher--launcher选项中=之后的部分。

Cross-building交叉建设

Since the image includes native binaries, you will need to create an image for each platform.由于映像包含本机二进制文件,因此您需要为每个平台创建一个映像。 Obviously, one option is to build the image on a Linux system, and again on a Windows system, and again on a Mac, etc.显然,一种选择是在 Linux 系统上构建映像,然后在 Windows 系统上构建映像,然后再在 Mac 上构建,等等。

But you can also use jmod and jlink to create images for other platforms, regardless of where you're building.但你也可以使用jmodjlink创建其他平台的图像,无论在哪里,你的建设。

There's only a few additional steps needed.只需要几个额外的步骤。 First, you will need the JDKs for those other platforms.首先,您将需要用于其他平台的 JDK。 Download them as archives (zip or tar.gz), not installers, and unpack them to directories of your choice.将它们下载为存档文件(zip 或 tar.gz),而不是安装程序,然后将它们解压到您选择的目录中。

Each JDK defines a platform string.每个 JDK 都定义了一个平台字符串。 This is normally of the form <os>-<arch>.这通常是 <os>-<arch> 的形式。 The platform is a property of the java.base module;平台是java.base模块的一个属性; you can see any JDK's platform by examining that module:您可以通过检查该模块来查看任何 JDK 平台:

jmod describe path-to-foreign-jdk/jmods/java.base.jmod | grep '^platform'

Pass that platform string to your jmod command using the --target-platform option:使用--target-platform选项将该平台字符串传递给您的 jmod 命令:

mkdir build/modules
jmod create --target-platform windows-amd64 \
    --class-path build/mybusinessapp.jar \
    --main-class com.mcs75.desktop.businessapp.BusinessApplication \
    build/modules/mybusinessapp.jmod

Finally, when linking, you want to explicitly include the other JDK's jmods directory, so jlink won't implicitly include its own JDK's modules:最后,在链接时,您希望显式包含其他 JDK 的jmods目录,因此jmods不会隐式包含其自己的 JDK 模块:

jlink --output build/image \
    --module-path path-to-foreign-jdk/jmods:build/modules:/opt/gluon-javafx-windows/lib \
    --add-modules com.mcs75.businessapp \
    --launcher MyBusinessApp=com.mcs75.businessapp

José: I see we have the same problem to solve (see my question at Self-Contained Application Packaging for a JavaFX app on Windows: InnoSetup OR javafxpackager? ). José:我发现我们有同样的问题需要解决(请参阅我在Windows上的JavaFX应用程序的自包含应用程序打包中的问题:InnoSetup或javafxpackager? )。 Perhaps, using the javapackager of a JDK would be an "easy" solution, despite of the numerous parameters. 也许,使用JDK的javapackager将是一个“简单”的解决方案,尽管有许多参数。 In my case, I have succeeded to generate a "Self-Contained Application Packaging for a JavaFX app on Windows" by this tool? 就我而言,我已成功通过此工具为Windows上的JavaFX应用程序生成“自包含应用程序打包”?

To make things easier use an IDE, I use Intellij Idea;为了使使用 IDE 更容易,我使用 Intellij Idea; build automation, I use gradle;构建自动化,我使用 gradle; and jlink plugin, I use the badass Jlink plugin - https://badass-jlink-plugin.beryx.org/releases/latest/#jpackageImage .和 jlink 插件,我使用了 badass Jlink 插件 - https://badass-jlink-plugin.beryx.org/releases/latest/#jpackageImage You can then easily use jlink and jpackage.然后您可以轻松使用 jlink 和 jpackage。 Just a thought.只是一个想法。

While other answers have a lot of details on how to do this manually, it's also possible to use a build tool plugin, eg, for Maven.虽然其他答案有很多关于如何手动执行此操作的详细信息,但也可以使用构建工具插件,例如,用于 Maven。

Using the JavaPackager Maven plugin, for example, simply adding something like this in your POM might already be enough:例如,使用JavaPackager Maven 插件,只需在 POM 中添加类似的内容就足够了:

<plugin>
    <groupId>io.github.fvarrui</groupId>
    <artifactId>javapackager</artifactId>
    <version>1.6.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>package</goal>
            </goals>
            <configuration>
                <mainClass>...</mainClass>
                <bundleJre>true</bundleJre>
                <platform>windows</platform>
            </configuration>
        </execution>
    </executions>
</plugin>

Running mvn package will generate a directory inside "target" that contains the EXE, a "jre" directory and a "libs" directory.运行mvn package将在“target”中生成一个包含 EXE 的目录、一个“jre”目录和一个“libs”目录。 The plugin can also generate an installer file.该插件还可以生成安装程序文件。

Obviously, for different platforms you need to adjust the config, but I tested this on Windows with JDK 16 and 17, and it seems to work fine.显然,对于不同的平台,您需要调整配置,但我在使用 JDK 16 和 17 的 Windows 上对此进行了测试,它似乎工作正常。

Try install4j. 试试install4j。 It may be your saviour. 它可能是你的救世主。

Check this article to see the suffering of another developer: 查看本文以了解其他开发人员的痛苦:

https://voyagergames.com/distributing-a-desktop-java-application/ https://voyagergames.com/distributing-a-desktop-java-application/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM