JavaFX 11: создание файла jar с помощью Gradle

Я пытаюсь обновить проект JavaFX с версии Java 8 до версии 11. Он работает, когда я использую задачу Gradle "запустить" (я следовал руководству Openjfx), но когда я создаю (с помощью задачи Gradle «jar») и выполняю (с помощью «java -jar») файл jar, появляется сообщение «Ошибка: компоненты времени выполнения JavaFX отсутствуют и необходимы для запуска этого приложения».

Вот мой файл build.gradle:

group 'Project'
version '1.0'
apply plugin: 'java'
sourceCompatibility = 1.11

repositories {
    mavenCentral()
}

def currentOS = org.gradle.internal.os.OperatingSystem.current()
def platform
if (currentOS.isWindows()) {
    platform = 'win'
} else if (currentOS.isLinux()) {
    platform = 'linux'
} else if (currentOS.isMacOsX()) {
    platform = 'mac'
}
dependencies {
    compile "org.openjfx:javafx-base:11:${platform}"
    compile "org.openjfx:javafx-graphics:11:${platform}"
    compile "org.openjfx:javafx-controls:11:${platform}"
    compile "org.openjfx:javafx-fxml:11:${platform}"
}

task run(type: JavaExec) {
    classpath sourceSets.main.runtimeClasspath
    main = "project.Main"
}

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

compileJava {
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

run {
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

Вы знаете, что мне делать?


person Flpe    schedule 29.09.2018    source источник


Ответы (3)


С Java / JavaFX 11 теневая / толстая банка не будет работать.

Как вы можете прочитать здесь:

Эта ошибка исходит от sun.launcher.LauncherHelper в модуле java.base. Причина этого в том, что приложение Main расширяет Application и имеет метод main. В этом случае LauncherHelper проверит, присутствует ли модуль javafx.graphics как именованный модуль:

Optional<Module> om = ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME);

Если этого модуля нет, запуск прерывается. Следовательно, в этом случае нельзя использовать библиотеки JavaFX в качестве jar-файлов в пути к классам.

Более того, каждый jar-файл JavaFX 11 имеет файл module-info.class на корневом уровне.

Когда вы объединяете все содержимое банок в одну толстую банку, что происходит с этими файлами с одинаковыми именами и одинаковым расположением? Даже если в толстой банке все они хранятся, как это можно определить как единый модуль?

Есть запрос в поддержку этого, но он еще не рассмотрен: http://openjdk.java.net/projects/jigsaw/spec/issues/#MultiModuleExecutableJARs

Предоставляет средства для создания исполняемого модульного «uber-JAR», который содержит более одного модуля, сохраняя идентичность модулей и границы, так что все приложение может быть отправлено как единый артефакт.

Плагин shadow по-прежнему имеет смысл объединить все ваши другие зависимости в одну банку, но в конце концов вам придется запустить что-то вроде:

java --module-path <path-to>/javafx-sdk-11/lib \
   --add modules=javafx.controls -jar my-project-ALL-1.0-SNAPSHOT.jar

Это означает, что, в конце концов, вам придется установить JavaFX SDK (для каждой платформы), чтобы запустить эту банку, которая использовала зависимости JavaFX из maven central.

В качестве альтернативы вы можете попробовать использовать jlink для создания облегченной JRE, но ваше приложение должно быть модульным.

Также вы можете использовать Javapackager для создания установщика для каждой платформы. См. http://openjdk.java.net/jeps/343, который создаст упаковщик для Java. 12.

Наконец, есть экспериментальная версия Javapackager, которая работает с Java 11 / JavaFX 11: http://mail.openjdk.java.net/pipermail/openjfx-dev/2018-September/022500.html

ИЗМЕНИТЬ

Поскольку средство запуска Java проверяет, расширяет ли основной класс javafx.application.Application, и в этом случае ему требуется среда выполнения JavaFX, доступная в виде модулей (а не в виде jar-файлов), возможным обходным путем для его работы должно быть добавление нового Главный класс, который будет основным классом вашего проекта, и этот класс будет тем, который вызывает ваш класс JavaFX Application.

Если у вас есть javafx11 пакет с классом Application:

public class HelloFX extends Application {

    @Override
    public void start(Stage stage) {
        String javaVersion = System.getProperty("java.version");
        String javafxVersion = System.getProperty("javafx.version");   
        Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");
        Scene scene = new Scene(new StackPane(l), 400, 300);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

Затем вам нужно добавить этот класс в этот пакет:

public class Main {

    public static void main(String[] args) {
        HelloFX.main(args);
    }
}

И в вашем файле сборки:

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

Теперь вы можете запустить:

./gradlew run

or

./gradlew jar
java -jar build/libs/javafx11-1.0-SNAPSHOT.jar

JavaFX из FAT jar

Конечная цель - иметь модули JavaFX как именованные модули на пути к модулю, и это похоже на быстрое / уродливое решение для тестирования вашего приложения. Для распространения все же предлагаю вышеупомянутые решения.

person José Pereda    schedule 29.09.2018
comment
Вы проверяли код, указанный в ответе? Это должно сработать для вас, и тогда вы должны таким же образом обновить свой код. В противном случае создайте новый вопрос. - person José Pereda; 10.11.2018
comment
Ага, ошибка моя. В методе Main::main я вызвал HelloFX.launch(args), что недопустимо, вместо HelloFX.main(args). Вызов HelloFX.launch(HelloFX.class, args) в порядке, поэтому метод HelloFX::main в этом случае избыточен (например, IDE могут его использовать). - person user1803551; 10.11.2018
comment
@ JoséPereda, это все еще ответ? Нет обновлений? - person FearX; 23.10.2019
comment
Я пробовал использовать только ваше редактирование, но у меня есть исключения: Exception in thread "main" java.lang.NoClassDefFoundError: javafx/application/Application - person FearX; 23.10.2019
comment
@FearX этот ответ обновлен для использования подключаемого модуля JavaFX gradle. - person José Pereda; 23.10.2019

[Изменить: для последних версий JavaFX, пожалуйста, проверьте мой второй ответ]

Если кому-то интересно, я нашел способ создавать файлы jar для проекта JavaFX11 (с модулями Java 9). Я тестировал его только в Windows (если приложение предназначено также для Linux, я думаю, что нам нужно проделать те же шаги, но в Linux, чтобы получить jar-файлы JavaFX для Linux).

У меня есть модуль «Project.main» (созданный IDEA, когда я создавал проект Gradle):

 src
 +-- main
 |   +-- java
     |   +-- main
         |   +-- Main.java (from the "main" package, extends Application)
     |   +-- module-info.java
 build.gradle
 settings.gradle
 ...

Файл module-info.java:

module Project.main {
    requires javafx.controls;
    exports main;
}

Файл build.gradle:

plugins {
    id 'java'
}

group 'Project'
version '1.0'
ext.moduleName = 'Project.main'
sourceCompatibility = 1.11

repositories {
    mavenCentral()
}

def currentOS = org.gradle.internal.os.OperatingSystem.current()
def platform
if (currentOS.isWindows()) {
    platform = 'win'
} else if (currentOS.isLinux()) {
    platform = 'linux'
} else if (currentOS.isMacOsX()) {
    platform = 'mac'
}
dependencies {
    compile "org.openjfx:javafx-base:11:${platform}"
    compile "org.openjfx:javafx-graphics:11:${platform}"
    compile "org.openjfx:javafx-controls:11:${platform}"
}

task run(type: JavaExec) {
    classpath sourceSets.main.runtimeClasspath
    main = "main.Main"
}

jar {
    inputs.property("moduleName", moduleName)
    manifest {
        attributes('Automatic-Module-Name': moduleName)
    }
}

compileJava {
    inputs.property("moduleName", moduleName)
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls'
        ]
        classpath = files()
    }
}

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

Файл settings.gradle:

rootProject.name = 'Project'

И команды Gradle:

#Run the main class
gradle run

#Create the jars files (including the JavaFX jars) in the "build/libs" folder
gradle createJar

#Run the jar file
cd build/libs
java --module-path "." --module "Project.main/main.Main"
person Flpe    schedule 02.10.2018
comment
Я только определил файл build.gradle. Затем я просто использовал команду gradle (вы можете использовать ее, если вы установили Gradle). Если я хорошо помню, конфигурация была проще с последними версиями JavaFX, благодаря подключаемому модулю JavaFX в Gradle (openjfx.io/openjfx-docs/#gradle). Так что все эти шаги, вероятно, сейчас бесполезны. - person Flpe; 23.10.2019
comment
Мы можем особенно использовать jlink для получения файла приложения (раздел Runtime images) - person Flpe; 23.10.2019
comment
Я не понимаю. После применения плагина и установки build.gradle мне просто нужно запустить задачу jLink, а что я должен раздавать своим клиентам? Папка сборки? я запутался - person FearX; 23.10.2019
comment
С помощью описанного выше метода (не jlink) банку можно найти в build / libs. Я полагаю, что с помощью jlink вы можете найти сгенерированный распространяемый файл (zip-файл, содержащий файлы .bat и .sh для запуска приложения, если я хорошо помню) в подпапке build. - person Flpe; 23.10.2019
comment
Ага. Мне не нужна JRE на компьютере клиента? - person FearX; 23.10.2019
comment
Он нужен вам, если вы создаете файл jar (метод без jlink). С jlink он вам не нужен (он включает легкую JRE, содержащую только основные модули Java, которые вам нужны) - person Flpe; 23.10.2019
comment
Спасибо, Флпе. Я использую Jlink, но у меня возникли проблемы с распространением его в Windows - person FearX; 23.10.2019
comment
Я сделал пример, используя Jlink. Если вам интересно, проверьте новый ответ, который я добавлю к этому вопросу - person Flpe; 24.10.2019
comment
@Flpe Мне не удалось заставить это работать. Без добавления каких-либо осложнений Groovy я обнаружил, что gradle run не работает, а gradle createJar работает, но команда java просто зависает. Ошибка от gradle run: Ошибка: компоненты среды выполнения JavaFX отсутствуют и необходимы для запуска этого приложения. Я получаю это как в W10, так и в Linux, и я пробовал как Eclipse, так и Intellij. - person mike rodent; 08.03.2020

В последних версиях JavaFX вы можете использовать два плагина Gradle для простого распространения вашего проекта (javafxplugin и jlink).

С помощью этих плагинов вы можете:

  • Создайте распространяемый zip-файл, содержащий все необходимые файлы jar: для его выполнения требуется JRE (с помощью bash или пакетного сценария)
  • Создайте собственное приложение с Jlink для данной ОС: JRE не требуется для его выполнения, поскольку Jlink включает в себя «легкую» JRE (включая только необходимые java-модули и зависимости) в папке распространения.

Я сделал пример на битбакете, если вам нужен пример.

person Flpe    schedule 24.10.2019
comment
Большой. Любой желающий может также использовать задачу Gradle installdist, которая создаст каталоги / bin и / lib, поэтому вам не нужно распаковывать tar / zip, как в случае с assemble. Команда jlink также произвела некоторый вывод, но не создала образ каталога при сборке. Я ничего не знаю о том, что с этим происходит, но первый вариант меня устраивает. - person mike rodent; 05.03.2020
comment
Я пробовал использовать это с Java 11 (в настоящее время моя версия ОС) ... Это отлично работает в интерфейсе командной строки, но Eclipse жалуется, что больше не может находить классы FX. Есть идеи, почему это может быть? - person mike rodent; 05.03.2020
comment
Вы импортировали проект как проект Gradle? Вероятно, это не сработает, если вы откроете его как обычный проект. Я использовал Intellij IDEA для создания этого примера приложения (так что оно должно работать как минимум с IDEA) - person Flpe; 05.03.2020
comment
Спасибо ... Я запустил его как проект Gradle, да. Я сузил проблему после большого количества возни: пожалуйста, посмотрите этот вопрос, который я только что задал: stackoverflow.com/q/60552847 / 595305. - person mike rodent; 05.03.2020
comment
Отличный шаблон, спасибо. Вы должны попросить ребят из jlink добавить его в раздел примеров. - person norbertas.gaulia; 09.12.2020
comment
Спасибо за комплимент, приятно видеть, что он полезен. Полагаю, мне нужно немного улучшить файл README и комментарии, прежде чем просить добавить их в раздел примеров. Но спасибо за идею. - person Flpe; 10.12.2020