JavaFX -如何使用maven编译uber jar并创建安装程序?

toe95027  于 2023-11-17  发布在  Maven
关注(0)|答案(2)|浏览(163)

我需要将一个JavaFX 20应用程序编译成一个可执行的Jar文件,其中包括pom.xml文件中列出的所有依赖项。(包括javaFX本身和所有其他的JAR文件)。我的项目是基于Maven的。我尝试了许多不同的Maven插件,但没有一个能够通过双击使最终的jar可执行,虽然我能够在命令行中使用java -jar命令运行。目的是在Linux上使用next/next/finish安装程序分发这个应用程序,Windows和MacOS。最终用户配置文件是一个实验室研究员,IT知识较低(我为一家帮助保护亚马逊森林的NPO工作)。是否有客观的方法来做到这一点?
我已经尝试了许多不同目标的maven插件(资源,依赖,阴影,编译器等),但都没有成功。

owfi6suc

owfi6suc1#

自包含应用

创建安装程序的愿望有助于创建一个完全独立的应用程序。这是一个不仅捆绑了您的代码及其第三方依赖项,而且还捆绑了Java运行时环境本身的应用程序。您的最终用户不需要单独安装Java。

GraalVM原生镜像

创建自包含应用程序的一个选项是GraalVM's native image。请注意,这也会提前将Java代码编译为本机代码。
我对GraalVM的经验很少,所以我不会给予一个例子,但这里有一些链接可能会有所帮助:

jpackage

jpackage是创建自包含应用程序和安装程序的一个相对容易使用的工具。它自版本16起随JDK提供。有关详细信息,请参阅Packaging Tool User Guide。它还有助于理解jlink,因为jpackage将使用jlink“幕后”生成自定义运行时映像,或者显式使用jlink,然后为jpackage提供自定义运行时映像。
然而,使用jpackage的一个显著缺点是,它只能为其运行的操作系统创建应用程序映像和安装程序。如果你想要一个Windows应用程序,那么你必须在Windows上构建你的项目,对于Linux和macOS也是如此。正如jewelsea在评论中解释的那样,还有一些特定于平台的问题你必须解决:
为多个平台创建安装程序是很棘手的,每个平台都有自己的怪癖,你需要处理。(我相信是付费的)开发人员签署证书。所以准备好花一些时间,(可能)需要一点钱来创建安装程序。虽然有现有的指南,比如我引用的那些,可能仍然会有一些平台和应用程序特定的怪癖,你需要在有限的外部帮助下自己解决。
如果你要创建一个自包含的应用程序,那么我建议你放弃创建一个fat/uber/shadowed的安装文件。一个自包含的应用程序已经包含了所有的依赖项,所以一个shadowed的安装文件不太可能提供任何好处。另外,如果你要使用jpackage来创建安装程序,你最好从一开始就围绕jpackage设计你的整个部署。
使用Maven配置文件可以解决许多特定于平台的配置问题。而任何剩余的特定于平台的配置,以及在多个平台上构建应用程序并自动发布它,都可以通过某种CI/CD管道来完成。尽管如此,请记住永远不要向您的配置文件提交任何机密信息。(例如,Git),通过扩展意味着不将任何秘密放在POM文件中。

示例

下面是一个POM,它被配置为在激活windows配置文件时创建msi安装程序文件。注意,在这个例子中只有Windows的配置。org.panteleyev:jpackage-maven-plugin插件用于执行jpackage
此POM是为非模块化应用程序设计的。因此,它被配置为将JavaFX模块添加到自定义运行时映像中,而项目代码和任何其他依赖项被配置为通过--input--main-jar放置在类路径上。通过在自定义运行时映像中放置JavaFX,它将隐式地位于模块路径上。

  • 免责声明:**我对Gradle比Maven更熟悉,因此可能有方法简化以下POM。
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>app</artifactId>
  <version>1.0</version>

  <name>app</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <javafxVersion>21.0.1</javafxVersion>
    <libsDir>${project.build.directory}/artifacts/libs</libsDir>
    <javafxModsDir>${project.build.directory}/artifacts/javafx</javafxModsDir>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.openjfx</groupId>
      <artifactId>javafx-controls</artifactId>
      <version>${javafxVersion}</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <!-- Lock in versions. -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.3.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.3.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.11.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.3.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>3.6.1</version>
        </plugin>
        <plugin>
          <groupId>org.panteleyev</groupId>
          <artifactId>jpackage-maven-plugin</artifactId>
          <version>1.6.0</version>
        </plugin>
      </plugins>
    </pluginManagement>

    <plugins>
      <!-- 
        Configure JAR plugin to set Main-Class entry in the JAR's manifest. Also, put the JAR
        file in the same directory as the non-JavaFX dependencies to make using jpackage easier.
      -->
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <outputDirectory>${libsDir}</outputDirectory>
          <archive>
            <manifest>
              <mainClass>com.example.app.Main</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>

      <!-- Copy dependencies into project-local directories to make using jpackage easier. -->
      <plugin>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <!-- 
            Put JavaFX JARs in their own directory so they are not included in the input directory
            and can easily be placed on the module-path of jpackage.
          -->
          <execution>
            <id>copy-javafx-deps</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${javafxModsDir}</outputDirectory>
              <includeGroupIds>org.openjfx</includeGroupIds>
            </configuration>
          </execution>
          <!-- 
            Put all non-JavaFX JARs in a separate directory to use with the input argument
            of jpackage. The project JAR will be placed here as well.
          -->
          <execution>
            <id>copy-nonjavafx-deps</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${libsDir}</outputDirectory>
              <excludeGroupIds>org.openjfx</excludeGroupIds>
            </configuration>
          </execution>
        </executions>
      </plugin>

      <!-- Common jpackage configurations. -->
      <plugin>
        <groupId>org.panteleyev</groupId>
        <artifactId>jpackage-maven-plugin</artifactId>
        <configuration>
          <modulePaths>
            <modulePath>${javafxModsDir}</modulePath>
          </modulePaths>
          <!-- 
            Must explicitly add modules since project code is not modular, meaning there's
            no module-info.java file with requires directives.

            Include the jdk.localedata module to ensure internationalization works. You can filter
            which locales are included via jlink options.
          -->
          <addModules>javafx.controls,jdk.localedata</addModules>
          <input>${libsDir}</input>
          <mainJar>${project.name}-${project.version}.jar</mainJar>
          <temp>${project.build.directory}/jpackage/temp</temp>
        </configuration>
      </plugin>
    </plugins>

  </build>

  <profiles>

    <profile>
      <id>windows</id>
      <build>
        <plugins>
          <!-- Windows-specific jpackage configurations. -->
          <plugin>
            <groupId>org.panteleyev</groupId>
            <artifactId>jpackage-maven-plugin</artifactId>
            <executions>
              <execution>
                <id>windows-msi</id>
                <phase>package</phase>
                <goals>
                  <goal>jpackage</goal>
                </goals>
                <configuration>
                  <type>MSI</type>
                  <destination>${project.build.directory}/jpackage/windows-msi</destination>
                  <winPerUserInstall>true</winPerUserInstall>
                  <winDirChooser>true</winDirChooser>
                  <winUpgradeUuid>7c59c875-1ad3-4042-9c9f-fed5fc3f8ab9</winUpgradeUuid>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>

  </profiles>

</project>

字符串
下面是一个com.example.app.Main类的例子,它可以沿着POM:

package com.example.app;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        // List view will display the modules included in the custom run-time image.
        var listView = new ListView<String>();
        ModuleLayer.boot()
            .modules()
            .stream()
            .map(Module::getName)
            .sorted()
            .forEach(listView.getItems()::add);
        primaryStage.setTitle("JavaFX " + System.getProperty("javafx.version") + " Application");
        primaryStage.setScene(new Scene(listView, 600, 400));
        primaryStage.show();        
    }
}


要创建msi文件,请执行:

mvn -P windows package


你应该得到一个target/jpackage/windows-msi/app-1.0.msi安装程序文件。
请注意,您必须在Windows上执行此操作,并且必须安装WiX Toolset 3.x.x(4.x.x版本似乎不能与jpackage一起工作,或者至少我不能让它工作)。如果你不想安装WiX 3.x.x只是为了运行示例,那么将类型更改为<type>APP_IMAGE</type>。你不会得到安装程序文件,但最终得到的是一个可以按原样运行的应用程序映像。

可执行脚本

您可以使用Maven Shade Plugin通过Maven使用JavaFX创建一个可执行的fat/uber/shadow文件。注意,这将固有地将所有内容放在类路径上。任何明确依赖于模块的代码都可能会在此设置中中断,尽管这种代码很少见。此外,JavaFX在技术上不支持从类路径加载。据我所知,这样做不会破坏JavaFX 21的任何内容,但是JavaFX开发人员不太可能修复此配置导致的任何问题。
请参阅David Weber's answer以获取使用Maven Shade插件的示例。

如果您想使用隐藏的可执行Java文件作为部署策略,那么我建议您放弃创建安装程序。(你可以把安装程序发给他们),然后他们就可以翻倍单击*.jar文件使其运行。如果Java安装程序提供了该选项,请确保最终用户启用了将*.jar文件与Java关联。
如果你想要一个跨平台的可执行JavaScript文件,你必须为每个平台手动声明一个JavaFX依赖项。JavaFX依赖于特定于平台的本机代码,Maven工件将该本机代码嵌入到JavaScript文件中,但仅适用于特定平台。例如,javafx-graphics-21.0.1-win.jar包含Windows的本机代码,但不包含Linux或macOS。
请注意,当JavaFX在class-path上时,就像这里的情况一样,那么你的主类不能javafx.application.Application的子类。你必须有一个单独的“launcher class”作为主类。类似于:

package sample;

import javafx.application.Application;

public class Launcher {

    public static void main(String[] args) {
        Application.launch(YourAppClass.class, args);
    }
}

5jdjgkvh

5jdjgkvh2#

前言:

1.点击GitHub链接。
1.阅读GitHub上的简短文档。
1.看一下代码。
对于Gradle项目,请查看build. gradle。对于Maven项目,请查看pom.xml。

创建胖目录的解决方案:

使用gradle和fat/uber任务运行的JavaFX应用程序示例:https://github.com/davidweber411/JavaFxAppGradleNonModular
使用maven和fat/uber的JavaFX应用程序示例,目标为:https://github.com/davidweber411/JavaFxAppMavenNonModular

创建代理的解决方案:

1.使用jpackage来完成这个任务。不要使用jlink,也不要使用其他的东西。Jpackage才是正确的选择。

  1. https://docs.oracle.com/en/java/javase/17/docs/specs/man/jpackage.html
    您可以在此应用程序中查看为Windows应用程序生成的jpackage命令:https://github.com/davidweber411/Java2NativeWinConverter

JavaFxProjectGenerator:

也许你可以从这个整洁的工具中获益:https://github.com/davidweber411/JavaFxProjectGenerator
您可以使用Maven或Gradle创建完整的预定义JavaFX应用程序,两者都包括用于创建fat/uber插件的预配置任务/目标。
注意:它会生成一个JDK 17项目,但您可以在构建管理系统文件中对其进行更改。
网站链接:https://wedasoft.com/projects/javafx-project-generator/#

相关问题