javafx的maven项目打jar包配置

项目结构

.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   ├── com
│   │   │   │   └── bingbaihanji
│   │   │   │       ├── Main.java     	──────────────────────────────> 继承 javafx.application.Application
│   │   │   │       ├── StartApplication.java         ────────────────> 启动类  Main.main(args);
│   │   │   │       ├── data
│   │   │   │       │   └── CpuLoadMetricService.java
│   │   │   │       └── view
│   │   │   │           ├── CustomStage.java
│   │   │   │           └── SystemAreaChartView.java
│   │   │   └── module-info.java
│   │   └── resources
│   │       ├── chart-styles.css
│   │       ├── close.png
│   │       ├── cpu.png
│   │       ├── logback.xml
│   │       ├── max.png
│   │       └── min.png
│   └── test
│       └── java
│           └── com
│               └── bingbiahanji
│                   └── SimpleBigInteger.java

1.使用使用maven打包插件 maven-shade-plugin 将项目打包成fatjar

完整 pom.xml

<?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.bingbaihanji</groupId>
    <artifactId>fxoshi</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>21</java.version>
        <javafx.version>21.0.4</javafx.version>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.github.oshi</groupId>
            <artifactId>oshi-core</artifactId>
            <version>6.6.5</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.36</version>
        </dependency>

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

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-swing</artifactId>
            <version>${javafx.version}</version>
        </dependency>

        <!-- slf4j与logback集成配置 -->
        <!-- slf4j日志门面 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.9</version>
        </dependency>
        <!-- logback 日志实现-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.4.14</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.14</version>
        </dependency>
        <!--logback提供HTTP访问日志功能-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>1.4.14</version>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>javafx-windows</id>
            <activation>
                <os>
                    <family>windows</family>
                </os>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-controls</artifactId>
                    <version>${javafx.version}</version>
                    <classifier>win</classifier>
                </dependency>
                <dependency>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-fxml</artifactId>
                    <version>${javafx.version}</version>
                    <classifier>win</classifier>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>javafx-linux</id>
            <activation>
                <os>
                    <family>linux</family>
                </os>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-controls</artifactId>
                    <version>${javafx.version}</version>
                    <classifier>linux</classifier>
                </dependency>
                <dependency>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-fxml</artifactId>
                    <version>${javafx.version}</version>
                    <classifier>linux</classifier>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>javafx-macos</id>
            <activation>
                <os>
                    <family>mac</family>
                </os>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-controls</artifactId>
                    <version>${javafx.version}</version>
                    <classifier>mac</classifier>
                </dependency>
                <dependency>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-fxml</artifactId>
                    <version>${javafx.version}</version>
                    <classifier>mac</classifier>
                </dependency>
            </dependencies>
        </profile>
    </profiles>


    <build>
        <plugins>
            <!-- Maven Compiler Plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <source>21</source> <!-- 替换为项目使用的 JDK 版本 -->
                    <target>21</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.36</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.6</version>
                <configuration>
                    <mainClass>com.bingbaihanji.Main</mainClass>
                </configuration>
            </plugin>

            <!-- Maven Shade Plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.4.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <!-- 指定 Main-Class -->
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.bingbaihanji.StartApplication</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

在项目根目录下执行 mvn packagetarget 目录下生成fatjar 可以直接使用java -jar 方式启动

其中 MANIFEST.MF 内容为

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.8.5
Built-By: 冰白寒祭
Build-Jdk: 21.0.5
Main-Class: com.bingbaihanji.StartApplication

此jar包可以通过graalvm 打包成平台对应的二进制可执行文件

Graalvm通过静态分析提前编译来为Java应用程序构建高度优化的本机可执行文件,这就需要在编译时就知道所有的程序类型,而java中的jni、反射、动态代理等功能,在编译时不确定具体的类型,所以在使用GraalVm构建native image前需要通过配置列出反射可见的所有类型。而程序内置提供了agentlib

具体步骤如下

1 使用agentlib打包跟踪jvm生成配置文件
java -agentlib:native-image-agent=config-output-dir=.\${artifactId}\ -jar [jar包]

这里的 {artifactId} 为 jar包的 artifactId,执行后会在{artifactId} 目录下生成 配置文件。

2 配置jar包

在jar包里边的META-INF文件夹中创建native-image文件夹,在native-image新增带有groupid(com.bingbaihanji)和artifactId(将生成的${artifactId}复制到native-image目录下)

3 编译成平台二进制可执行文件

在配置好c编译语言环境的终端中执行 native-image -jar [jar包] --no-fallback --static

编译完成后可直接运行二进制可执行文件

其中

--no-fallback build stand-alone image or report failure [构建独立镜像(不依赖环境jvm),如果失败,则报告错误]

--static build statically linked executable (requires static libc and zlib) [构建静态链接的可执行文件(需要静态版本的 libc 和 zlib)]

若 windows 环境报错 Native-image building on Windows currently only supports target architecture: AMD64 (?? unsupported)

需要设置 Visual Studio 中只保留英文语言包

2.使用maven打包插件 maven-dependency-plugin 将项目打包成分散的jar包 (项目启动包和依赖库)

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.10.1</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>libs/</classpathPrefix>
                        <mainClass>com.bingbaihanji.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>
                            ${project.build.directory}/libs
                        </outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

然后在当前项目下运行 mvn package即可打包完成 在 target 目录下生成 libs 和 项目的jar包,

其中libs是项目运行时所依赖的库,其中项目jar包的清单文件内容为

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.8.5
Built-By: 冰白寒祭
Build-Jdk: 17.0.2
Class-Path: libs/javafx-controls-17.0.2.jar libs/javafx-controls-17.0.2-
 win.jar libs/javafx-graphics-17.0.2.jar libs/javafx-graphics-17.0.2-win
 .jar libs/javafx-base-17.0.2.jar libs/javafx-base-17.0.2-win.jar libs/j
 avafx-fxml-17.0.2.jar libs/javafx-fxml-17.0.2-win.jar
Main-Class: com.bingbaihanji.Main

如果此时直接用 java -jar 命令来运行项目jar包会提示 错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序

是由于从 jdk9 开始 java开始添加了以模块化来构建项目的规范;

解决办法有

1.可以添加jvm参数

可以在当前路径下执行

java -jar --module-path ./libs --add-modules javafx.controls,javafx.fxml ./javaFxDemo.jar

来启动项目。

2.在项目中添加一个启动类

maven打包的时候主类指定为启动类

修改maven插件配置

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>libs/</classpathPrefix>
                <mainClass>com.bingbaihanji.Start</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

新建一个启动类

package com.bingbaihanji;
import com.bingbaihanji.Main;
/**
 * @author 冰白寒祭
 * @date 2023-12-03 00:09:51
 * @description //TODO
 */
public class Start {
    public static void main(String[] args) {
        Main.main(args);
    }
}

重新打包后即可直接用 java -jar 的方式来启动项目

3.使用springboot的maven打包插件[简单快捷]来打成一个可执行的jar包

maven的pom.xml文件配置
<?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>

    <!--1.引入springboot的父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.bingbaihanji</groupId>
    <artifactId>javaFxDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>17.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>17.0.2</version>
        </dependency>
    </dependencies>

    <build>
        <!--修改编译出来的jar包名,仅为{artifactId}.jar-->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!--springboot的打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

启动类

加入 @SpringBootApplication 注解和启动函数

@SpringBootApplication
public class Main extends Application {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
        Application.launch(args);
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        ······
    }
}

然后在当前项目下运行 mvn package 即可在当前目录下生成的target目录中找到可以执行的jar包

jar包大致结构如下

.
├── BOOT-INF
│   ├── classes
│   │   └── com
│   │       └── bingbaihanji
│   │           └── Main.class   // 项目启动类
│   ├── classpath.idx
│   ├── layers.idx
│   └── lib  // 项目运行所依赖的jar包
├── META-INF
│   ├── MANIFEST.MF // 项目清单文件
│   └── maven
│       └── com.bingbaihanji
│           └── javaFxDemo
│               ├── pom.properties
│               └── pom.xml
├── module-info.class
└── org // springboot框架提供的用来启动jar相关的类
    └── springframework
        └── boot
            └── loader

19 directories, 97 files

MANIFEST.MF 文件结构

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.3.0
Build-Jdk-Spec: 17
Implementation-Title: javaFxDemo
Implementation-Version: 1.0-SNAPSHOT
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.bingbaihanji.Main
Spring-Boot-Version: 3.1.2
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

人生不作安期生,醉入东海骑长鲸