代码之家  ›  专栏  ›  技术社区  ›  taranion

打包一个非模块化的JavaFX应用程序

  •  1
  • taranion  · 技术社区  · 7 年前

    我有一个Java8应用程序,它使用JavaFX并在其中扩展了主类 javafx.application.application . 目前,我将它作为一个胖罐子交付,它在OracleJava8上运行良好。

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

    1. http://mail.openjdk.java.net/pipermail/openjfx-dev/2018-June/021977.html

    而我可以和1一起去。作为一种解决方法,我想知道目前(OpenJDK 11)构建/交付非模块化JavaFX应用程序的可执行fat jar的预期方式是什么。有人能帮忙吗?

    1 回复  |  直到 7 年前
        1
  •  46
  •   José Pereda    7 年前

    以下是打包/分发(非模块化)JavaFX11终端应用程序的几个选项。其中大多数都在官方OpenJFX中进行了解释 docs .

    我会用 this sample 作为参考。我还将使用Gradle。Maven(不同的插件)也可以做类似的事情,甚至不需要构建工具(但不建议这样做…)。构建工具现在是必须的。

    肥罐

    这仍然是一个有效的选项,但不是首选选项,因为它打破了模块化设计,将所有东西捆绑在一起,并且除非您注意到这一点,否则它不是跨平台的。

    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) }
        }
    }
    

    注意使用 Launcher 班如OP提到或解释的 here Application 现在需要创建一个胖罐子。

    ./gradlew jar 生成一个胖jar(~8MB),其中包括JavaFX类和 你的 当前平台。

    你可以跑 java -jar build/libs/hellofx.jar 和往常一样,但只在同一个平台上。

    如OpenJFX文档或 here ,您仍然可以创建一个跨平台jar。

    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(19MB),可以分发到这三个平台。

    (注意,媒体和Web也有平台相关的代码/本机库)。

    因此,它在Java8上的工作方式与以前一样。但正如我之前所说的,它破坏了模块的工作方式,也不符合当今图书馆和应用程序的分布方式。

    不要忘记,这些JAR的用户仍然需要安装JRE。

    jlink

    那么,在您的项目中分发一个自定义映像怎么样?它已经包括一个本机JRE和一个启动器了?

    你会说,如果你有一个非模块化的项目,那是行不通的。符合事实的但在讨论jpackage之前,让我们先看看这里的两个选项。

    运行时插件

    这个 badass-runtime-plugin 是一个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']
    }
    

    ./gradlew runtime 它将创建一个运行时及其启动器,以便您可以运行:

    cd build/image/hellofx/bin
    ./hellofx
    

    注意,它依赖于shadow插件,并且还需要一个启动器类。

    如果你跑 ./gradlew runtimeZip

    同样,您可以将此zip分发给具有相同平台的任何用户,但现在不需要安装JRE。

    看见 targetPlatform 用于为其他平台构建图像。

    模块化

    我们一直认为我们有非模块化的项目,这是无法改变的。。。但如果我们真的改变了呢?

    模块化 这不是一个很大的改变:你添加了一个 module-info.java 描述符,并在其上包含所需的模块,即使这些模块是非模块化JAR(基于自动名称)。

    基于相同的示例,我将添加一个描述符:

    module hellofx {
        requires javafx.controls;
    
        exports hellofx;
    }
    

    现在我可以使用 jlink badass-gradle-plugin 是一个gradle插件,与前面提到的作者相同,允许创建自定义运行时。

    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'
    

    您现在可以运行:

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

    ./gradlew jlinkZip

    正如您所看到的,不需要影子插件或启动器类。您还可以针对其他平台,或者包括非模块依赖项,如下面所示 question .

    包装

    到目前为止,还没有GA版本(可能我们必须等待Java 13),但现在有两个选项可用于Java 11或12:

    在Java/JavaFX11中,您可以找到Java 12上JPackager的初始工作中的一个后端端口 here . 有一篇关于使用它的好文章 here ,以及使用它的gradle项目 here .

    对于Java/JavaFX12,已经有了 build 0 version jpackage Java 13将提供的工具。

    这是该工具的初步使用:

    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}"
    }
    

    正在运行 ./gradlew jpackage 生成一个dmg(65 MB),我可以将其分发以安装:

    installer

    结论

    虽然您可以坚持使用经典的胖罐子,但当迁移到Java11及更高版本时,一切都应该是模块化的。新的(即将推出的)可用工具和插件,包括IDE支持,在这一转变过程中起到了帮助作用。

    我知道我在这里介绍了最简单的用例,当尝试更复杂的实际案例时,会有几个问题。。。但我们应该更好地解决这些问题,而不是继续使用过时的解决方案。

    推荐文章