JavaFX 11: создайте JAR-файл с помощью Gradle
Я пытаюсь обновить проект JavaFX с версии 8 Java до версии 11. Это работает, когда я использую задачу "Выполнить " Gradle ( я следовал учебному руководству Openjfx), но когда я собираю (с помощью задачи "jar" Gradle) и выполняю (с "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'
]
}
}
Ты знаешь, что я должен делать?
2 ответа
Если кому-то интересно, я нашел способ создания jar-файлов для проекта JavaFX11 (с модулями Java 9). Я тестировал его только на Windows (если приложение также для Linux, я думаю, что мы должны сделать те же шаги, но на Linux, чтобы получить JavaFX jar для 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"
В Java/JavaFX 11 теневая / жирная банка не будет работать.
Как вы можете прочитать здесь:
Эта ошибка исходит от
sun.launcher.LauncherHelper
в модуле java.base. Причина в том, что приложение Main расширяетсяApplication
и имеет основной метод. Если это так, тоLauncherHelper
проверит наличие модуля javafx.graphics в качестве именованного модуля:
Optional<Module> om = ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME);
Если этот модуль отсутствует, запуск отменяется. Следовательно, использование библиотек JavaFX в качестве jar-файлов на пути к классам в этом случае не допускается.
Более того, каждая банка JavaFX 11 имеет module-info.class
файл, на корневом уровне.
Когда вы объединяете все содержимое банок в одну толстую банку, что происходит с этими файлами с тем же именем и в том же месте? Даже если толстая банка хранит их все, как это идентифицировать как единый модуль?
Есть запрос на поддержку этого, но он еще не был обработан: http://openjdk.java.net/projects/jigsaw/spec/issues/
Предоставьте средство для создания исполняемого модульного "uber-JAR", который содержит более одного модуля, сохраняя идентификаторы и границы модулей, так что все приложение может быть доставлено как один артефакт.
Плагин shadow по-прежнему имеет смысл объединить все остальные зависимости в один jar, но в конце концов вам придется запустить что-то вроде:
java --module-path <path-to>/javafx-sdk-11/lib \
--add modules=javafx.controls -jar my-project-ALL-1.0-SNAPSHOT.jar
Это означает, что, в конце концов, вам придется установить JavaFX SDK (для каждой платформы), чтобы запустить тот jar, который использовал зависимости 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-файлов), возможный обходной путь, чтобы заставить его работать, - добавление нового класса Main, который будет основным классом вашего проекта, и этот класс будет тот, который вызывает ваш класс JavaFX Application.
Если у тебя есть javafx11
пакет с классом приложения:
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
или же
./gradlew jar
java -jar build/libs/javafx11-1.0-SNAPSHOT.jar
Конечная цель состоит в том, чтобы модули JavaFX были названы как модули на пути к модулю, и это выглядит как быстрый / безобразный обходной путь для тестирования вашего приложения. Для распространения я бы все же предложил вышеупомянутые решения.
В последних версиях JavaFX вы можете использовать два плагина Gradle для простого распространения вашего проекта (javafxplugin и jlink).
С помощью этих плагинов вы можете:
- Создайте распространяемый zip-файл, содержащий все необходимые файлы jar: для его выполнения требуется JRE (с помощью bash или пакетного сценария)
- Создайте собственное приложение с Jlink для данной ОС: JRE не требуется для его выполнения, поскольку Jlink включает в себя "легкую" JRE (включая только необходимые java-модули и зависимости) в папке распространения.
Я сделал пример на битбакете, если вам нужен пример.