简体   繁体   English

打包非模块化JavaFX应用程序

[英]Package a non-modular JavaFX application

I have a Java 8 application, that uses JavaFX and where the main class extends javafx.application.Application . 我有一个Java 8应用程序,它使用JavaFX,主类扩展了javafx.application.Application Currently, I deliver it as a fat jar and it runs fine on Oracle Java 8. 目前,我将它作为胖罐提供,它在Oracle Java 8上运行良好。

Now I want it to be able to run on OpenJDK 11. To add JavaFX, I already added the artifacts from org.openjfx to the classpath and am including them in the fat jar. 现在我希望它能够在OpenJDK 11上运行。要添加JavaFX,我已经将org.openjfx中的工件添加到类路径中,并将它们包含在胖jar中。 If I start my jar from the command line, I get 如果我从命令行启动我的jar,我明白了

Error: JavaFX runtime components are missing, and are required to run this
application

I found two possible ways around this problem: 我找到了解决此问题的两种可能方法:

  1. The dirty one: Write a special launcher that does not extend Application and circumvent the module check. 脏的:编写一个特殊的启动器,它不会扩展应用程序并绕过模块检查。 See http://mail.openjdk.java.net/pipermail/openjfx-dev/2018-June/021977.html 请参阅http://mail.openjdk.java.net/pipermail/openjfx-dev/2018-June/021977.html
  2. The clean one: Add --module-path and --add-modules to my command line. 干净的:将--module-path和--add-modules添加到我的命令行。 The problem with this solution is, that I want my end users to be able to just launch the application by double clicking it. 这个解决方案的问题是,我希望我的最终用户能够通过双击启动应用程序。

While I could go with 1. as a workaround, I wonder what is currently (OpenJDK 11) the intended way to build/deliver executable fat jars of non-modular JavaFX applications. 虽然我可以使用1.作为一种解决方法,但我想知道目前(OpenJDK 11)构建/交付非模块化JavaFX应用程序的可执行胖罐的方法。 Can anyone help? 有人可以帮忙吗?

These are a few options for packaging/distributing a (non-modular) JavaFX 11 end application. 这些是用于打包/分发(非模块化)JavaFX 11终端应用程序的一些选项。 Most of them are explained in the official OpenJFX docs . 其中大多数都在官方的OpenJFX 文档中进行了解释。

I'll use this sample as reference. 我将使用此示例作为参考。 I'll also use Gradle. 我也会用Gradle。 Similar can be done with Maven (different plugins), and even without build tools (but this is not recommended...). 类似的可以使用Maven(不同的插件),甚至没有构建工具(但不建议这样做......)。 Build tools are a must nowadays. 如今,构建工具是必须的。

Fat Jar 胖罐子

This is still a valid option, but not the preferred one, as it breaks the modular design and bundles everything all together, and it is not cross-platform unless you take care of that. 这仍然是一个有效的选项,但不是首选的选项,因为它打破了模块化设计并将所有内容捆绑在一起,除非您处理它,否则它不是跨平台的。

For the given sample, you have a build.gradle file like this: 对于给定的示例,您有一个build.gradle文件,如下所示:

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.5'
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx.HelloFX'

jar {
    manifest {
        attributes 'Main-Class': 'hellofx.Launcher'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

Note the use of a Launcher class. 请注意使用Launcher类。 As mentioned by the OP or explained here , a launcher class that not extends from Application is required now to create a fat jar. 正如OP所提到或在此解释的那样,现在需要一个不从Application扩展的启动器类来创建一个胖罐。

Running ./gradlew jar produces a fat jar (~8 MB) that includes the JavaFX classes, and the native libraries of your current platform. 运行./gradlew jar产生脂肪罐子(约8 MB),其中包括JavaFX类,和当前平台的本地库。

You can run java -jar build/libs/hellofx.jar as usual, but only in the same platform. 您可以像往常一样运行java -jar build/libs/hellofx.jar ,但只能在同一平台上运行。

As explained in the OpenJFX docs or here , you can still create a cross-platform jar. 正如OpenJFX文档或此处所述 ,您仍然可以创建跨平台jar。

In this case, we can include the three graphics jars, as those are the ones that have platform-dependent code and libraries. 在这种情况下,我们可以包含三个图形jar,因为那些具有平台相关的代码和库。 Base, controls and fxml modules are the platform-independent. Base,controls和fxml模块是独立于平台的。

dependencies {
    compile "org.openjfx:javafx-graphics:11.0.1:win"
    compile "org.openjfx:javafx-graphics:11.0.1:linux"
    compile "org.openjfx:javafx-graphics:11.0.1:mac"
}

./gradlew jar will produce a fat jar (19 MB) now that can be distributed to these three platforms. ./gradlew jar可以生产一个胖罐(19 MB),可以分发到这三个平台。

(Note Media and Web have also platform-dependent code/native libraries). (注意媒体和Web也有平台相关的代码/本机库)。

So this works as it used to on Java 8. But as I said before, it breaks how modules work, and it doesn't align on how libraries and apps are distributed nowadays. 所以这就像在Java 8上一样。但正如我之前所说的那样,它打破了模块的工作方式,并且它与当今的库和应用程序的分布方式并不一致。

And don't forget that the users of these jars will still have to install a JRE. 并且不要忘记这些罐子的用户仍然需要安装JRE。

jlink JLINK

So what about distributing a custom image with your project, that already includes a native JRE and a launcher? 那么如何使用您的项目分发自定义图像,其中已包含本机JRE和启动器?

You will say that if you have a non-modular project, that won't work. 你会说,如果你有一个非模块化项目,那将无法工作。 True. 真正。 But let's examine two options here, before talking about jpackage. 但在讨论jpackage之前,我们先来看看两个选项。

runtime-plugin 运行时,插件

The badass-runtime-plugin is a Gradle plugin that creates runtime images from non-modular projects. badass-runtime-plugin是一个Gradle插件,可以从非模块化项目中创建运行时映像。

With this build.gradle: 使用此build.gradle:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.5'
    id 'org.beryx.runtime' version '1.0.0'
    id "com.github.johnrengelman.shadow" version "4.0.3"
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx.Launcher'

runtime {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
}

When you run ./gradlew runtime it will create a runtime, with its launcher, so you can run: 运行./gradlew runtime ,它将使用其启动器创建运行时,因此您可以运行:

cd build/image/hellofx/bin
./hellofx

Note it relies on the shadow plugin, and it requires a Launcher class as well. 注意它依赖于shadow插件,它也需要一个Launcher类。

If you run ./gradlew runtimeZip , you can get a zip for this custom image of about 32.5 MB. 如果运行./gradlew runtimeZip ,则可以为此自定义映像获取大约32.5 MB的zip。

Again, you can distribute this zip to any user with the same platform, but now there is no need of an installed JRE. 同样,您可以将此zip分发给具有相同平台的任何用户,但现在不需要安装JRE。

See targetPlatform for building images for other platforms. 请参阅targetPlatform以构建其他平台的映像。

Going Modular 走向模块化

We keep thinking that we have non-modular project, and that can't be changed... but what if we do change it? 我们一直认为我们有非模块化项目,而且无法改变......但是如果我们改变它会怎样?

Going modular is not that big of a change: you add a module-info.java descriptor, and you include the required modules on it, even if those are non-modular jars (based on automatic names). 模块化并不是一个很大的变化:你添加一个module-info.java描述符,并在其上包含所需的模块,即使它们是非模块化的jar(基于自动名称)。

Based on the same sample, I'll add a descriptor: 基于相同的示例,我将添加一个描述符:

module hellofx {
    requires javafx.controls;

    exports hellofx;
}

And now I can use jlink on command line, or use a plugin for it. 现在我可以在命令行上使用jlink ,或者为它使用插件。 The badass-gradle-plugin is a gradle plugin, from the same author as the one mentioned before, that allows creating a custom runtime. badass-gradle-plugin是一个gradle插件,来自与前面提到的同一作者,允许创建自定义运行时。

With this build file: 使用此构建文件:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.5'
    id 'org.beryx.jlink' version '2.3.0'
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx/hellofx.HelloFX'

you can run now: 你现在可以跑了:

./gradlew jlink
cd build/image/bin/hellofx
./hellofx

or ./gradlew jlinkZip for a zipped version (31 MB) that can be distributed and run in machines, of the same platform, even if there is no JRE installed. 或者./gradlew jlinkZip用于压缩版本(31 MB),即使没有安装JRE,也可以在同一平台的机器中分发和运行。

As you can see, no need for shadow plugin or Launcher class. 如您所见,不需要shadow插件或Launcher类。 You can also target other platforms, or include non-modular dependencies, like in this question . 您也可以定位其他平台,或者包含非模块化依赖项,例如此问题

jpackage JPackage上

Finally, there is a new tool to create executable installers that you can use to distribute your application. 最后,还有一个新工具可用于创建可用于分发应用程序的可执行安装程序。

So far there is no GA version yet (probably we'll have to wait for Java 13), but there are two options to use it now with Java 11 or 12: 到目前为止还没有GA版本(可能我们将不得不等待Java 13),但现在有两种方法可以在Java 11或12中使用它:

With Java/JavaFX 11 there is a back port from the initial work on the JPackager on Java 12 that you can find here . 使用Java / JavaFX 11,您可以在此处找到Java 12上JPackager的初始工作的后端口。 There is a nice article about using it here , and a gradle project to use it here . 有一个关于使用它一个很好的文章在这里 ,和gradle这个项目中使用它在这里

With Java/JavaFX 12 there is already a build 0 version of the jpackage tool that will be available with Java 13. 使用Java / JavaFX 12,已经有一个可用于Java 13的jpackage工具的build 0版本

This is a very preliminary use of the tool: 这是该工具的初步使用:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.5'
}

repositories {
    mavenCentral()
}

dependencies {
}

javafx {
    version = "12-ea+5"
    modules = [ 'javafx.controls' ]
}

mainClassName = 'hellofx/hellofx.HelloFX'

def java_home = '/Users/<user>/Downloads/jdk-12.jdk/Contents/Home'
def installer = 'build/installer'
def appName = 'HelloFXApp'

task copyDependencies(type: Copy) {
    dependsOn 'build'
    from configurations.runtime
    into "${buildDir}/libs"
}

task jpackage(type: Exec) {
    dependsOn 'clean'
    dependsOn 'copyDependencies'

    commandLine "${java_home}/bin/jpackage", 'create-installer', "dmg",
            '--output', "${installer}", "--name", "${appName}",
            '--verbose', '--echo-mode', '--module-path', 'build/libs',
            '--add-modules', "${moduleName}", '--input', 'builds/libraries',
            '--class', "${mainClassName}", '--module', "${mainClassName}"
}

Now running ./gradlew jpackage generates a dmg (65 MB), that I can distribute to be installed: 现在运行./gradlew jpackage生成一个dmg(65 MB),我可以分发安装:

安装程序

Conclusion 结论

While you can stick to classic fat jars, when moving to Java 11 and beyond, everything is supposed to be modular. 虽然你可以坚持使用经典的胖罐,但是当转向Java 11及更高版本时,一切都应该是模块化的。 The new (soon to be) available tools and plugins, including the IDE support, are helping during this transition. 新的(即将推出)可用工具和插件(包括IDE支持)在此过渡期间提供帮助。

I know I've presented here the simplest use case, and that when trying more complex real cases, there will be several issues... But we should better work on solving those issues rather than keep using outdated solutions. 我知道我在这里介绍了最简单的用例,并且在尝试更复杂的实际案例时,会出现一些问题......但我们应该更好地解决这些问题,而不是继续使用过时的解决方案。

暂无
暂无

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

相关问题 JavaFX——模块化和非模块化的区别 - JavaFX - difference between modular and non-modular JavaFX真的可以在非模块化环境中使用吗? - Can JavaFX realistically be used in a non-modular environment? 如何使用非模块化依赖项在 Java 11 中打包应用程序 - How to package app at Java 11 with non-modular dependencies 创建非模块化应用程序是否需要我将模块化与非模块化依赖项分开 - Will creating non modular application require me to separate my modular from non-modular dependencies IntelliJ:在非模块化应用程序中的模块路径上放置一些依赖项 - IntelliJ: Put some dependencies on the module path in non-modular application 如何使用 maven 部署 JavaFX 项目,包括自定义和非模块化依赖项? - How to deploy a JavaFX project with maven, including custom and non-modular dependencies? JavaFX 非模块化项目图形设备初始化失败:d3d,sw 初始化 QuantumRenderer 时出错:找不到合适的管道 - JavaFX non-modular project Graphics Device initialization failed for : d3d, sw Error initializing QuantumRenderer: no suitable pipeline found 修复 java.lang.module.ResolutionException:两个模块将同一个包导出到非模块化应用程序中的另一个模块 - Fix java.lang.module.ResolutionException: two modules export the same package to another module in a non-modular app 在编译模块时,将非模块化 jars 添加到 -classpath 并将模块化 jars 添加到 --module-path 有什么价值? - What is the value of adding non-modular jars to the -classpath and modular jars to the --module-path when compiling a module? 使用Maven构建具有非模块化依赖关系的JDK 9项目需要什么 - What do I need to build JDK 9 project with non-modular dependencies using Maven
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM