Windows Java JavaFX IntelliJ IDEA 开发环境搭建 创建工程 编译运行 打包分发 自定义运行时

news2024/11/23 18:29:06

博文目录

文章目录

  • 本文说明
  • JavaFX 简单说明
    • JavaFX 版本说明
    • JavaFX 与 JDK 的关系
    • JavaFX 与 JDK Modular (JDK 9 模块化系统)
    • JavaFX 模块说明 (JavaFX 20)
    • JavaFX Scene Builder
    • 创建 JavaFX 应用程序的两种选择
  • 环境搭建
    • 版本选择
    • IntelliJ Idea 安装配置
    • Modular JavaFX Project (非 Maven / Gradle)
      • JavaFX SDK
      • 新建工程
      • 添加 JavaFX SDK 依赖
      • 关联 JavaFX SDK 源码
      • 创建第一个 JavaFX 窗体
        • HelloWorld.java
        • 错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序
        • 效果展示
    • Modular JavaFX Maven Project
      • 新建工程
      • 自带初始模板代码如下
        • pom.xml
        • module-info.java
        • HelloApplication.java
        • hello-view.fxml
        • HelloController.java
      • 运行 HelloApplication
  • Scene Builder 19.0.0 安装与配置
  • JavaFX 开发需要掌握的知识点
    • Java 模块化技术基础
    • JavaFX 基础编程模型
    • JavaFX MVC 架构
    • JavafX 事件处理机制
    • JavaFX 多窗体编程与数据传输
    • JavaFX 数据绑定机制
    • Java MVVM
    • JavaFX 多线程
    • JavaFX 集成第三方框架, 如 RxJava
    • JavaFX Chart 图表
  • 编译与运行
    • Modular JavaFX Project (非 Maven / Gradle)
      • 准备
      • 使用命令行工具编译
      • 使用命令行工具运行
    • Modular JavaFX Maven Project
      • 使用命令行工具编译与运行
      • 创建自定义 JRE
  • 自定义 JRE
    • jlink 参数列表
      • `重要参数说明`
    • 生成最小 JRE
    • 生成包含 java.se 模块的 JRE
    • 生成包含完整 JavaFX 模块的 JRE
      • 使用该 JRE 运行 JavaFX 程序, 以前面章节的 Modular Java Project 工程为例
    • 将自定义模块打包到 JRE 中, 并生成该模块中主类的启动程序命令, 以前面章节的 Modular Java Project 工程为例
  • 打包与分发
    • jpackage
      • wix v3
    • Modular JavaFX Project (非 Maven / Gradle)
      • 准备
      • 打包为安装程序
      • 打包为运行程序
        • 补充说明
    • Modular JavaFX Maven Project
      • 准备
      • 打包为运行程序
      • 打包为安装程序
    • Non-Modular Java Project
      • 通过 IDEA 将工程打包为可运行 Jar
      • 将可运行 Jar 打包为运行程序
      • 将可运行 Jar 打包为运行程序


本文说明

官网 Getting Started with JavaFX

本文基于 OpenJFX 官网文档编写, 建议先通读一遍上述引用的官网文档, 内容不算多, 然后与本文相互印证

官网文档是基于 cmd 创建的工程, 编译和运行也是在 cmd 中, 并没有使用集成开发环境

JavaFX 简单说明

JavaFX 官网

JavaFX 是一个开源的下一代客户端应用程序平台,适用于基于 Java 构建的桌面、移动和嵌入式系统。它是许多个人和公司的协作成果,目标是为开发富客户端应用程序生成一个现代、高效且功能齐全的工具包。

JavaFX 主要致力于富客户端开发,以弥补 swing 的缺陷,主要提供图形库与 media 库,支持 audio,video,graphics,animation,3D 等,同时采用现代化的 css 方式支持界面设计。同时又采用 XUI 方式以 XML 方式设计 UI 界面,达到显示与逻辑的分离。与 android 这方面确实有点相似性。

Java 8 新特性探究(十三)JavaFX 8 新特性以及开发 2048 游戏

跟 java 在服务器端和 web 端成绩相比,桌面一直是 java 的软肋,于是 Sun 公司在 2008 年推出 JavaFX,弥补桌面软件的缺陷,请看下图 JavaFX 一路走过来的改进
在这里插入图片描述
从上图看出,一开始推出时候,开发者需使用一种名为 JavaFX Script 的静态的、声明式的编程语言来开发 JavaFX 应用程序。因为 JavaFX Script 将会被编译为 Java bytecode,程序员可以使用 Java 代码代替。 JavaFX 2.0 之后的版本摒弃了 JavaFX Script 语言,而作为一个 Java API 来使用。因此使用 JavaFX 平台实现的应用程序将直接通过标准 Java 代码来实现。 JavaFX 2.0 包含非常丰富的 UI 控件、图形和多媒体特性用于简化可视化应用的开发,WebView 可直接在应用中嵌入网页;另外 2.0 版本允许使用 FXML 进行 UI 定义,这是一个脚本化基于 XML 的标识语言。

从 JDK 7u6 开始,JavaFX 就与 JDK 捆绑在一起了,JavaFX 团队称,下一个版本将是 8.0,目前所有的工作都已经围绕 8.0 库进行。这是因为 JavaFX 将捆绑在 Java 8 中,因此该团队决定跳过几个版本号,迎头赶上 Java 8。

目前我还不知道 Java 平台其他的更好的客户端库

JavaFX 版本说明

官网 版本说明

在这里插入图片描述

截止到 20230423, JavaFX 的版本如上图所示. 有几个点需要注意

  • JavaFX 11 和 17 是长期支持版本, 会有专人做更新和修复工作, 除此之外的版本都是非长期支持版本
  • JavaFX 11 虽然是长期支持版本, 但是其将在 202309 到期, 而且有更好的 17 可选, 所以 11 不推荐使用
  • JavaFX 20 是当前的最新发布版本, 但不是长期支持版本. 最新发布版本会有最多最全的新特性, 有需要可以使用该版本
  • JavaFX 21 是早期访问版本, 相当于测试版本, 一般不建议使用, 看中某些新特性尝鲜可以试试
  • 灰色的 JavaFX 12 / 13 / 14 / 15 / 16 / 18 已经不再更新, 不建议使用. 19 虽然还在更新, 但是估计用不了多久也会停止, 同样不建议使用

综上所述, 比较推荐 JavaFX 17 / 20, 17 有长期支持, 20 有最新最全的特性

JavaFX 与 JDK 的关系

JavaFX 建立在 JDK 之上,是一个独立的组件。

从 JDK 11 开始, JavaFX 与 JDK 分开发布, JavaFX 不再集成于 JDK 中

JavaFXJDK
2017 or later
1911 or later
1811 or later
1711 or later
1611 or later
1511 or later
1411 or later
1311 or later
1211 or later
11OpenJDK 10 / JDK 11 or later
8集成于 JDK 8

JavaFX 与 JDK Modular (JDK 9 模块化系统)

在 Java 8 之后,JavaFX 从 JDK 中分离出来,然后在 Java 9 时,Java 引入了 Java 模块化系统。从那之后,JavaFX 要求使用 Java 模块化系统来运行 JavaFX。因此,当直接使用 Java 8 以上的环境运行非模块化的 JavaFX 项目时就会出现如下报错。

错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序

解决方法见下文 错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序 部分

JavaFX 模块说明 (JavaFX 20)

官网 JavaFX 20 API 文档

  • javafx.base: 定义 JavaFX UI 工具包的基本 API,包括绑定、属性、集合和事件的 API。
  • javafx.controls: 定义可用于 JavaFX UI 工具包的 UI 控件、图表和外观。
  • javafx.fxml: 为 JavaFX UI 工具包定义 FXML API。
  • javafx.graphics: 为 JavaFX UI 工具包定义核心场景图 API(例如布局容器、应用程序生命周期、形状、转换、画布、输入、绘画、图像处理和效果),以​​及动画、CSS、并发、几何、打印的 API , 和开窗。
  • javafx.media: 定义用于播放媒体和音频内容的 API,作为 JavaFX UI 工具包的一部分,包括MediaView和 MediaPlayer.
  • javafx.swing: 为 JavaFX UI 工具包中包含的 JavaFX/Swing 互操作支持定义 API,包括SwingNode(用于将 Swing 嵌入 JavaFX 应用程序)和 JFXPanel(用于将 JavaFX 嵌入 Swing 应用程序)。
  • javafx.web: 为 JavaFX UI 工具包中包含的 WebView 功能定义 API。

JavaFX Scene Builder

官网 Scene Builder 下载

Scene Builder 是针对 JavaFX FXML UI 的拖拽式页面设计编码工具, 免费且开源

当前版本是 Scene Builder 19.0.0, 运行需要 JDK 11 or later

如果使用 JDK 8 的 JavaFX, 可以下载 Scene Builder 8.5.0

创建 JavaFX 应用程序的两种选择

  • Modular JavaFX Project (非 Maven / Gradle): 需要下载并引入 JavaFX SDK 提供的依赖 jar, 在 lib 目录下
  • Modular JavaFX Maven / Gradle Project: 使用构建工具可以从中央仓库下载指定版本的 JavaFX 相关依赖, 和使用 JavaFX SDK 本质是一样的, 但是无需下载 JavaFX SDK

建议选择第二种. 第一种是非常原始的方式, 可用于学习, 不适用于大工程开发

环境搭建

官网 JavaFX 和 IntelliJ IDEA

版本选择

  • IntelliJ IDEA 使用的是 2021.2.3
  • 本次学习使用最新版 JavaFX 20, 需要使用 JDK 17

IntelliJ Idea 安装配置

Modular JavaFX Project (非 Maven / Gradle)

以这种方式创建的工程默认是非模块化工程, 如果需要改成模块化工程, 需自行添加并编写 module-info.java, module-info.java 应放在源码根目录下

JavaFX SDK

官网 JavaFX SDK 下载

  • 先选择合适的 SDK 版本, 建议从 11 / 17 / 20 / Early Access 这 4 个版本中选择
  • 然后下载并解压该 SDK, 如 C:\mrathena\develop\javafx-sdk-20.0.1在这里插入图片描述

新建工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

初始工程结构如下

在这里插入图片描述

添加 JavaFX SDK 依赖

打开工程结构设置

在这里插入图片描述

确保当前使用的是 JDK 17

在这里插入图片描述
在这里插入图片描述

在 Libraries 里添加 JavaFX SDK 的 lib 目录, 其下所有 jar 文件都会被自动依赖到工程中

在这里插入图片描述
在这里插入图片描述

这里的 src.zip 就是 JavaFX SDK 的源码

在这里插入图片描述
在这里插入图片描述

关联 JavaFX SDK 源码

随便打开一个 JavaFX 的 class 文件, 如 javafx.application.Application, Idea 上方会提示 Choose sources ... 即可选择合适的源码关联, JavaFX SDK 的源码就是文件夹下的 src.zip

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建第一个 JavaFX 窗体

HelloWorld.java

package com.mrathena;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        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), 640, 480);
        stage.setScene(scene);
        stage.show();
    }

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

错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序

当使用 Java 8 以上的环境运行非模块化的 JavaFX 项目时就会出现如下报错

错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序

解决方法也有多种

  • 使用 JDK 8, 不建议, 不能从根源解决问题

  • 改造本工程为模块化工程, 推荐使用该方法. 添加 module-info.java 模块申明文件, 内容如下

    module com.mrathena { // 定义模块名称, 通常是类似包名的结构, 这里定义包名为 com.mrathena
        requires javafx.controls; // 引入 javafx.controls 模块
        exports com.mrathena; // 暴露本模块中的指定包(不包含子包) // Caused by: java.lang.IllegalAccessException: class com.sun.javafx.application.LauncherImpl (in module javafx.graphics) cannot access class com.mrathena.HelloWorld (in module com.mrathena) because module com.mrathena does not export com.mrathena to module javafx.graphics
    }
    
  • 在运行配置里添加 vm 参数 --module-path 下载的JavaFx的SDK的lib文件夹的完整路径 --add-modules javafx.controls,javafx.fxml, 如果路径里有空格, 则路径要加上双引号 "", 因为 JDK 不包含 JavaFX SDK, 所以可以人工关联上, 相当于给 JDK 补充了 JavaFX 的模块
    在这里插入图片描述
    为了方便, 可以给 JavaFX 运行模板配置该 vm 参数, 后续运行其他主类也会自动加上该参数
    在这里插入图片描述

  • 使用引导类, 在另一个类的 main 方法中调用主类的 main 方法, 会自动生成一个匿名的模块系统, 桌面程序可以运行, 但有警告提示

    4月 23, 2023 10:19:53 下午 com.sun.javafx.application.PlatformImpl startup
    警告: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @491dbe27'
    

效果展示

在这里插入图片描述

Modular JavaFX Maven Project

如果使用 Maven 开发 JavaFX 应用程序,则不必下载 JavaFX SDK。只需在 pom.xml 中指定想要的模块和版本,构建系统就会下载所需的模块,包括所属平台的本机库。本机库就是不同系统下的具体实现, 如下图中带 win 的依赖

在这里插入图片描述

  • D:\resource\develop\maven\repository\org\openjfx\javafx-controls\20.0.1\javafx-controls-20.0.1.jar
  • D:\resource\develop\maven\repository\org\openjfx\javafx-controls\20.0.1\javafx-controls-20.0.1-win.jar

新建工程

通过 Idea JavaFX Project 创建的就是 Maven 工程, 或者也可以创建 JavaFX Archetype 的 Maven 工程(需要自行导入该骨架), 不如 Idea JavaFX Project 来的方便

在这里插入图片描述
在这里插入图片描述

不知道这些依赖是干什么的先不选, 初始工程结构如下

在这里插入图片描述

默认就是 模块化结构的工程, pom.xml 中 javafx 的依赖版本默认是 17.0.0.1, 改成 20.0.1 即可

自带初始模板代码如下

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mrathena</groupId>
    <artifactId>javafx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>java.javafx.starter</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>20.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>20.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.9.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.7</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running with: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>com.mrathena.javafx/com.mrathena.javafx.HelloApplication</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

module-info.java

module com.mrathena.javafx {
    requires javafx.controls;
    requires javafx.fxml;

    opens com.mrathena.javafx to javafx.fxml;
    exports com.mrathena.javafx;
}

HelloApplication.java

package com.mrathena.javafx;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

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

hello-view.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.mrathena.javafx.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <Label fx:id="welcomeText"/>
    <Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>

HelloController.java

package com.mrathena.javafx;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class HelloController {
    @FXML
    private Label welcomeText;

    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to JavaFX Application!");
    }
}

运行 HelloApplication

在这里插入图片描述

Scene Builder 19.0.0 安装与配置

官网 Scene Builder 下载

下载好 SceneBuilder-19.0.0.msi 并运行, 选择合适的安装位置, 安装即可

在 Idea 里右键某个 fxml 文件, 选择使用 SceneBuilder 打开, 即可设置与 SceneBuilder.exe 的关联, 设置过后, 后续即可直接用 SceneBuilder 打开 fxml 文件, 编辑好后 Ctrl+S 保存即可直接作用于 fxml 文件

在这里插入图片描述
在这里插入图片描述

JavaFX 开发需要掌握的知识点

Java 模块化技术基础

51-Java模块化技术基础
廖雪峰 模块

  • module-info.java
  • –module-path: 依赖模块的查找目录
  • –add-modules: 指定除了默认模块额外要解析的模块
  • jmods: 用于生成 .jmod 格式的模块
  • jlink: 用于生成自定义 JRE 的工具, 可根据依赖自行精简 JRE, 便于打包和分发

JavaFX 基础编程模型

54-把握JavaFX编程模型

  • JavaFX 应用程序的主类需要继承自 javafx.application.Application 类, 然后重写其 start 方法, 此方法是所有 JavaFX 应用程序的入口点
  • JavaFX 应用程序将 UI 容器定义为 舞台(Stage)场景(Scene), Stage 是顶级容器, 它对应于窗体, 其内容有 Scene 决定. Scene 是所有可视化内容的 容器(Container). JavaFX 应用程序的可视化界面通常由 控件(Control)形状(Shape) 构成, 放到 Scene 中
  • JavaFX 中, Scene 中的内容会以由 图形节点(Node) 构成的分层 场景图(Scene Graph) 来展现. SceneGraph 其实就是一颗多叉树, 各种控件都是树中的节点, 最底层的节点通常是诸如按钮之类的控件, 也可以是 Circle 之类的图形. 拥有子树的节点称为容器, 在 JavaFX 中称为 布局(Layout)
  • Stage 与 Scene 是 1:1 的关系, Scene 与场景图的根节点是 1:1 的关系
  • JavaFX 应用程序的生命周期: init - start - running - stop, init / start / stop 由抽象类 javafx.application.Application 定义, 可以自定义覆盖
  • Stage 的生命周期: close / hide / show 等等, 可以给 Stage 挂接相关事件

JavaFX MVC 架构

55-JavaFX应用的MVC架构

  • MainClass: 程序的入口点, 通常包容管理多窗体, 实现各控制器之间相互通讯的相关代码
  • Model: 就是封装了数据的 JavaBean
  • View: 使用 FXML 编写, 包容可视化的 UI 控件, 使用户使用程序的媒介, 关联着一个控制器
  • Controller: 包容程序的应用逻辑, 负责响应用户操作, 负责在 View 和 Model 之间实现数据同步

JavafX 事件处理机制

56-把握JavaFX事件处理机制原理

  • 桌面应用都是 事件驱动 的, 事件(Event) 表示程序所感兴趣的某件事情发生了, 比如鼠标移动事件, 按键按下事件等
  • JavaFX 中, 事件是 javafx.event.Event 类或其任何子类的实例, JavaFX 提供了多种事件, 比如 DragEvent(拖动事件), KeyEvent, MouseEvent 等, JavaFX 提供的内置 UI 控件和图形对象, 都可以触发特定事件
  • JavaFX 中可以通过 Java 代码绑定事件, 也可以通过 FXML 绑定事件, 通过 FXML 声明的事件的方法要加 @FXML 注解
  • 事件派发链: 事件在控件树中的传播过程. 事件派发链是一个双向链表, 有时间目标对象负责创建, 事件发生时, 事件对象会在链中传播. 事件响应方法分为 EventFilter 和 EventHandler 两类, 先执行 EventFilter 事件链
    • EventFilter: 下沉: 从根节点向下来到事件发生节点
    • EventHandler: 冒泡: 从事件发生节点向上去往根节点
    • 举例: HBox 里放了个 Label: 两者都分别挂载了两个方向的 MOUSE_PRESSED 事件, 如果在标签上点击左键, 则会按照先 EventFilter 再 EventHandler 的顺序来调用响应方法, Filter 的先执行 hBox 的再执行 label 的, Handler 的先执行 label 的再执行 hBox 的
      • hBox.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {})
      • hBox.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {})
      • label.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {})
      • label.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {})

JavaFX 多窗体编程与数据传输

57-JavaFX多窗体编程

  • 每个 Stage 对应一个窗体
  • 模态窗体: 具有父子关系且子窗体出现时, 父窗体不能操作
  • 对象互相传递消息: 就是对象互相持有引用, 调用对方的方法来传递数据, 在 JavaFX 中窗体间传递信息靠的就是各 Controller 相互引用, 调用回调函数

JavaFX 数据绑定机制

58-JavaFX数据绑定机制及应用

  • JavaFX Bean Property: 提供了很多 SimpleIntegerProperty 之类的工具类, 可以绑定值改变事件, 代码操作值, 绑定的 UI 自动跟新
  • JavaFX 数据绑定编程模式
    • 绑定数据源: 具备改变通知能力的数据对象与数据集合, 包括 JavaFX Bean Property / ObservableValue / ObservableList<T>
    • JavaFX 应用程序的 UI 界面: 包容 TableView / Label 等支持数据绑定的控件
    • JavaFX 数据绑定机制: Binding 对象
    • 举例: Circle 始终居于窗体中央, Circle 自带圆心横纵坐标的 Property 属性, Scene 自带宽度高度的 Property 属性
      • circle.centerXProperty().bind(Bindings.divide(scene.widthProperty(), 2));
      • circle.centerYProperty().bind(Bindings.divide(scene.heightProperty(), 2));
  • 优点: 业务逻辑与 UI 分离, 绑定好关系之后, 属性值变化, UI 会自动更新, 无需代码修改 UI
  • 双向绑定:
    • 举例: 两个 TextField 保持内容一致, 再任何一方修改, 另一方保持内容相同
      • repeater.textProperty().bindBidirectional(speaker.textProperty());

Java MVVM

59-JavaFX实现MVVM架构

  • View: 还是 FXML 定义的 UI
  • ViewModel: 和 MVC 中的 Model 一样, 但是使用 JavaFX Bean Property / ObservableValue / ObservableList<T> 具备值变化通知能力的工具来做成员属性, 其与 UI 上的某些控件的值相对应
  • Controller: 实现 javafx.fxml.Initializable 接口, 在其提供的 initialize 方法中做如下事情
    • 控件的事件挂载
    • ViewModel 和 UI 控件做双向数据绑定, 这样 Controller 直接操作 ViewModel 的属性即可同步到 UI, 在 UI 相关控件上修改值也能同步到 ViewModel 的相关字段中
    • 注意: UI 变化完全由数据绑定后自行更新, Controller 不做任何 UI 的控制
  • 举例: 登录功能, UI 有 username 和 password 两个 TextField, login 和 cancel 两个 Button
    • LoginApplication: 程序主类入口
    • LoginViewModel: 持有 usernameProperty 和 passwordProperty 两个 StringProperty, 还有相关的 getter / setter / protertyGetter
    • LoginController: 实现 login 和 cancel 两个方法, 在 initialize 方法中做 login 按钮和 login 方法的绑定, cancel 按钮和 cancel 方法的绑定, ViewModel 中 usernameProperty 和 username 文本框的双向绑定, ViewModel 中 passwordProperty 和 password 文本框的双向绑定
      • login: 通过获取并验证 ViewModel 中 usernameProperty 和 passwordProperty 的值做合适的逻辑处理
      • cancel: 清空 ViewModel 中 usernameProperty 和 passwordProperty 的值
  • 也可以把 login 中的业务逻辑放到其他方法或类中, 这样 单元测试 时可以只测业务不看 UI

JavaFX 多线程

60-JavaFX多线程及典型示例展示

  • UI 操作有专门的 JavaFX Application Thread 线程负责, 自行创建的线程不允许操作 UI
  • 主线程中不要直接调用耗时很久的方法, 会导致窗体冻结. 要用一个线程去这些任务, 如果任务完成后需要更新 UI, 则可以使用 Platform.runLater(() - > {}) 的方式, 这里 Lambda 表达式执行的操作会被推送到 JavaFX Application Thread 线程中执行, 所以可以访问 UI 控件
  • JavaFX 的 Worker 和 Task: 提供了后台线程运行并更新 UI 的相关功能封装, 支持中途取消

JavaFX 集成第三方框架, 如 RxJava

  • RxJava: 一款响应式的框架
  • 需要注意一点, 需要更新 UI 的地方用 Platform.runLater

JavaFX Chart 图表

  • JavaFX 内置了 图表 相关控件, 支持数据绑定, 可做一些数据可视化相关功能

编译与运行

通过 Idea 可以便捷编译与运行, 直接点击 运行 按钮即可, 下面讲述通过 命令行 运行, 便于理解编译和运行的过程

Modular JavaFX Project (非 Maven / Gradle)

在这里插入图片描述

该工程非常简单, 如上图, 其中 .idea 和 javafx.iml 是 idea 的配置文件, out 是该工程的编译文件, 除此之外, 就是一个源码文件夹 src 了. 采用命令行的方式编译只需要 src 即可

准备

该工程对 JavaFX 的依赖来源于下载的 JavaFX SDK, 编译和运行时需要指定模块依赖

# 设置环境变量
set PATH_TO_FX="C:\mrathena\develop\javafx-sdk-20.0.1\lib"

# 切换工作路径设置到本工程内
cd C:\mrathena\develop\workspace\idea\mrathena\code.study\javafx

使用命令行工具编译

# 编译
dir /s /b src\*.java > sources.txt & javac --module-path %PATH_TO_FX% -d mods/javafx @sources.txt & del sources.txt

# &: 将多个操作连到一起, 写成一行命令

# dir: 列出当前目录下的所有文件夹和文件, 包含详细信息
# dir /s: 列出当前目录及其所有子目录下的所有文件和文件夹, 包含详细信息
# dir /b: 列出当前目录下的所有文件夹和文件, 只有文件名, 不包含其他信息
# dir /s /b: 列出当前目录及其所有子目录下的所有文件和文件夹的完整路径
# dir /s /b src\*.java: 列出当前目录下的 src 文件夹及其所有子文件夹下的 java 文件的完整路径
# > sources.txt: 将列出的 java 文件的完整路径保存到 sources.txt 文件中
dir /s /b src\*.java > sources.txt

# javac: java 编译工具
# javac -help: 查看 javac 的选项
# 用法: javac <options> <source files>
# @<filename>: 从文件读取选项和文件名, 如下方的 @sources.txt, 就是要从该文件中读取 javac 命令需要的 选项 和 源文件名称
# --module-path <path>, -p <path>: 指定查找应用程序模块的位置(依赖的模块的位置), 类似之前的 -classpath 指定依赖的位置
# -d <directory>: 指定放置生成的类文件的位置
# 指定 JavaFX SDK 下的 lib 为依赖模块的位置, 指定将生成的 class 文件存放在当前目录下的 mods/javafx 中, 指定源文件从 sources.txt 中读取
javac --module-path %PATH_TO_FX% -d mods/javafx @sources.txt

# 最后删掉生成的 sources.txt 文件
del sources.txt

注意: 编译生成的 mods/javafx 只是一个存放编译好的模块的文件夹而已, 可以是任意位置, 在 javafx 文件夹下的模块的名称由其中的 module-info.class 定义, 和文件夹名称没有关系. 本工程中我们定义的模块名称是 com.mrathena, 而不是 javafx

如果下载了 JavaFX JMods, 也可以用如下命令编译

cd C:\mrathena\develop\workspace\idea\mrathena\code.study\javafx
set PATH_TO_FX_MODS="C:\mrathena\develop\javafx-jmods-20.0.1"
dir /s /b src\*.java > sources.txt & javac --module-path %PATH_TO_FX_MODS% -d mods/javafx @sources.txt & del sources.txt

使用命令行工具运行

# java: java 运行工具
# 用法: java [options] <主类> [args...]: 执行类
# 用法: java [options] -jar <jar 文件> [args...]: 执行 jar 文件
# 用法: java [options] -m <模块>[/<主类>] [args...] / java [options] --module <模块>[/<主类>] [args...]: 执行模块中的主类
# 用法: java [options] <源文件> [args]: 执行单个源文件程序. 现在能直接执行源文件了?

# --module-path <模块路径>...:  用 ; 分隔的目录列表, 每个目录都是一个包含模块的目录。除了依赖的 JavaFX SDK 的 lib 外, 还依赖上刚刚编译出的放到 mods 文件夹下的模块
# java -m <模块>[/<主类>] [args...] / java [options] --module <模块>[/<主类>] [args...]: 执行 com.mrathena 模块下的 com.mrathena.HelloWorld 主类
java --module-path %PATH_TO_FX%;mods -m com.mrathena/com.mrathena.HelloWorld

Modular JavaFX Maven Project

该工程 pom.xml 中引入了 javafx-maven-plugin, 提供了 javafx:runjavafx:jlink 两个 maven 命令, 可以在 Idea 的 Maven 面板中的 Plugins 中找到 javafx, 运行该命令, 也可以使用命令行工具运行该命令

在这里插入图片描述

使用命令行工具编译与运行

cd C:\mrathena\develop\workspace\idea\mrathena\code.study\java.javafx.starter
mvn clean javafx:run

创建自定义 JRE

mvn clean javafx:jlink

默认会在 target 目录下生成一个 image 文件夹, 该文件夹就是生成的 JRE 了, 同样包含了自定义的 com.mrathena.javafx 模块, 可通过如下命令运行自定义模块 com.mrathena.javafx 中的主类 com.mrathena.javafx.HelloApplication

target\image\bin\java -m com.mrathena.javafx/com.mrathena.javafx.HelloApplication

也可以配置 javafx-maven-plugin, 做一些定制, 如下方的 configuration 部分, 指定了生成的 JRE 的文件夹为 jre, 指定了自定义模块的主类, 同时在 bin 目录额外生成了一个用于运行主类的 launcher 文件, 执行该文件即可便捷地运行主类

<plugin>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-maven-plugin</artifactId>
    <version>0.0.7</version>
    <configuration>
        <jlinkImageName>jre</jlinkImageName>
        <launcher>launcher</launcher>
        <mainClass>com.mrathena.javafx/com.mrathena.javafx.HelloApplication</mainClass>
    </configuration>
    <executions>
        <execution>
            <!-- Default configuration for running with: mvn clean javafx:run -->
            <id>default-cli</id>
            <configuration>
                <mainClass>com.mrathena.javafx/com.mrathena.javafx.HelloApplication</mainClass>
            </configuration>
        </execution>
    </executions>
</plugin>
target\jre\bin\launcher

自定义 JRE

使用 jlink 可以定制化 JRE, 可以做到如下的一些事情

  • 可以排除一些没有使用到的模块, 缩小 JRE 的体积以便于分发
  • 可以将 JDK 和 JavaFX 的相关模块打包到一起便于 JavaFX 应用程序的运行
  • 可以把我们自己自定义的模块也打包进去, 便于自定义模块的运行

jlink 参数列表

jlink --help
用法: jlink <选项> --module-path <模块路径> --add-modules <模块>[,<模块>...]
可能的选项包括:
      --add-modules <mod>[,<mod>...]    	除了初始模块之外要解析的根模块。<mod> 还可以为 ALL-MODULE-PATH。
      --bind-services                  	 	链接服务提供方模块及其被依赖对象
  -c, --compress=<0|1|2>                	Enable compression of resources:
                                          	Level 0: No compression
                                          	Level 1: Constant string sharing
                                          	Level 2: ZIP
      --disable-plugin <pluginname>     	Disable the plugin mentioned
      --endian <little|big>               	所生成 jimage 的字节顺序 (默认值: native)
  -h, --help, -?                        	输出此帮助消息
      --ignore-signing-information        	在映像中链接已签名模块化 JAR 的情况下隐藏致命错误。已签名模块化 JAR 的签名相关文件将不会复制到运行时映像。
      --launcher <名称>=<模块>[/<主类>]		为模块和主类添加给定名称的启动程序命令 (如果指定)
      --limit-modules <模块>[,<模块>...]  	限制可观察模块的领域
      --list-plugins                    	List available plugins
  -p, --module-path <path>              	模块路径。如果未指定,将使用 JDK 的 jmods 目录(如果存在该目录)。如果指定,但它不包含 java.base 模块,则将添加 JDK 的 jmods 目录(如果存在该目录)。
      --no-header-files                 	Exclude include header files
      --no-man-pages                    	Exclude man pages
      --output <路径>						输出路径的位置
      --save-opts <文件名>					将 jlink 选项保存在指定文件中
  -G, --strip-debug                     	Strip debug information
      --suggest-providers [<名称>,...]  	建议可从模块路径中实现给定服务类型的提供方
  -v, --verbose                         	启用详细跟踪
      --version								版本信息
      @<文件名>								从文件中读取选项

重要参数说明

  • --module-path: 依赖模块的查找路径, 默认值是 JDK 的 jmods 目录, 如果指定了该选项, 但目录中不包含 java.base 模块, 将会自动添加 JDK 的 jmods 目录
  • --add-modules: 将指定的模块及其可以传递到的依赖模块都打包到新的 JRE 中

生成最小 JRE

java.base 模块是 JDK 中最基础的模块, 是唯一一个没有引用其他任何模块的模块, 该模块暴露出了 JavaSE 的核心工具, 如java.lang / java.io / java.math / java.text / java.time / java.util 等

# 生成只包含 java.base 模块的 JRE
jlink --add-modules java.base --output jre

# 查看该 JRE, 只有一个 java.base 模块
jre\bin\java --list-modules
java.base@17.0.5

看了一下还有 40MB 大小

生成包含 java.se 模块的 JRE

java.se 模块是一个聚合模块, 该模块没有任何代码, 只有一个 module-info.class 文件, 用于声明一些依赖, 产生聚合的作用, 达到引用该模块就相当于引用很多其他模块的效果

不建议直接引用 java.se 模块,因为它就相当于 Java 9 以前版本的 rt.jar 的内容

# 检查 java.se 中的模块
java --describe-module java.se
java.se@17.0.5
requires java.instrument transitive
requires java.desktop transitive
requires java.transaction.xa transitive
requires java.security.jgss transitive
requires java.management transitive
requires java.prefs transitive
requires java.security.sasl transitive
requires java.xml transitive
requires java.sql transitive
requires java.naming transitive
requires java.datatransfer transitive
requires java.base mandated
requires java.xml.crypto transitive
requires java.scripting transitive
requires java.logging transitive
requires java.compiler transitive
requires java.net.http transitive
requires java.rmi transitive
requires java.management.rmi transitive
requires java.sql.rowset transitive

# 生成包含 java.se 所依赖的全部模块的 JRE
jlink --add-modules java.se --output jre

# 查看该 JRE 中包含的模块
jre\bin\java --list-modules
java.base@17.0.5
java.compiler@17.0.5
java.datatransfer@17.0.5
java.desktop@17.0.5
java.instrument@17.0.5
java.logging@17.0.5
java.management@17.0.5
java.management.rmi@17.0.5
java.naming@17.0.5
java.net.http@17.0.5
java.prefs@17.0.5
java.rmi@17.0.5
java.scripting@17.0.5
java.se@17.0.5
java.security.jgss@17.0.5
java.security.sasl@17.0.5
java.sql@17.0.5
java.sql.rowset@17.0.5
java.transaction.xa@17.0.5
java.xml@17.0.5
java.xml.crypto@17.0.5

看了一下还有 82MB 大小

生成包含完整 JavaFX 模块的 JRE

官网 JavaFX SDK / JMods 下载

生成包含 JavaFX 模块的 JRE 需要 JavaFX JMods, 下载后解压到合适的地方

# 设置环境变量(当然也可以不设置, 在使用该环境变量的地方用具体路径替代)
set PATH_TO_FX="C:\mrathena\develop\javafx-sdk-20.0.1\lib"
set PATH_TO_FX_MODS="C:\mrathena\develop\javafx-jmods-20.0.1"

# 检查 JavaFX 模块的声明
java --module-path %PATH_TO_FX% --describe-module javafx.base
java --module-path %PATH_TO_FX% --describe-module javafx.controls
# ...

# 生成包含 java.se 和 JavaFX 全部模块的 JRE
jlink --module-path %PATH_TO_FX_MODS% --add-modules java.se,javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.swing,javafx.web --output jre
# 根据依赖传递申明, 可简写为
jlink --module-path %PATH_TO_FX_MODS% --add-modules java.se,javafx.web,javafx.swing,javafx.fxml --output jre

# 查看该 JRE 中包含的模块
java.base@17.0.5
java.compiler@17.0.5
java.datatransfer@17.0.5
java.desktop@17.0.5
java.instrument@17.0.5
java.logging@17.0.5
java.management@17.0.5
java.management.rmi@17.0.5
java.naming@17.0.5
java.net.http@17.0.5
java.prefs@17.0.5
java.rmi@17.0.5
java.scripting@17.0.5
java.se@17.0.5
java.security.jgss@17.0.5
java.security.sasl@17.0.5
java.sql@17.0.5
java.sql.rowset@17.0.5
java.transaction.xa@17.0.5
java.xml@17.0.5
java.xml.crypto@17.0.5
javafx.base@20.0.1
javafx.controls@20.0.1
javafx.fxml@20.0.1
javafx.graphics@20.0.1
javafx.media@20.0.1
javafx.swing@20.0.1
javafx.web@20.0.1
jdk.jsobject@17.0.5
jdk.unsupported@17.0.5
jdk.unsupported.desktop@17.0.5
jdk.xml.dom@17.0.5

通过这种方式生成的 JRE, 已经包含了 JavaFX 相关依赖模块, 可以用来直接运行 JavaFX 应用程序, 可以分发给其他用户, 而其他用户不需要再下载 JavaFX SDK / JMods

使用该 JRE 运行 JavaFX 程序, 以前面章节的 Modular Java Project 工程为例

# 编译
dir /s /b src\*.java > sources.txt & javac --module-path %PATH_TO_FX% -d mods/javafx @sources.txt & del sources.txt

# 运行, 不再需要 --module-path 指向 PATH_TO_FX
jre\bin\java --module-path mods --module com.mrathena/com.mrathena.HelloWorld
jre\bin\java --module-path mods --m com.mrathena/com.mrathena.HelloWorld

将自定义模块打包到 JRE 中, 并生成该模块中主类的启动程序命令, 以前面章节的 Modular Java Project 工程为例

会将该自定义模块依赖的其他模块一同打包到 JRE 中

# 编译
dir /s /b src\*.java > sources.txt & javac --module-path %PATH_TO_FX% -d mods/javafx @sources.txt & del sources.txt

# 打包 JRE
jlink --module-path %PATH_TO_FX_MODS%;mods --add-modules com.mrathena --output jre
# 运行, 不再需要 --module-path
jre\bin\java -m com.mrathena/com.mrathena.HelloWorld

# 打包 JRE 并生成主类启动器, 在 JRE 的 bin 下生成一个 run 指令, 用于启动指定的主类
jlink --module-path %PATH_TO_FX_MODS%;mods --add-modules com.mrathena --output jre --launcher run=com.mrathena/com.mrathena.HelloWorld
# 运行, 不再需要指定主类
jre\bin\run

JRE 中集成了我们的自定义模块功能, 将该 JRE 发给别人, 即可直接运行功能

打包与分发

  • Fat Jar: 运行时不再需要 -classpath / --module-path 参数, 将依赖 Jar 中的相关类抽取到目标 Jar 包中
  • jpackage: Java 14 提供的打包分发工具, Windows 平台需要 Wix 3.0 及以上版本的支持
  • 自定义的包含 JDK 和 JavaFX 依赖模块的 JRE 里因为没有 jpackage 工具, 所以不能使用, 需要使用 JDK 的 jpackage 工具, 所以需要 --module-path 来补充 JavaFX 的依赖模块, 可参考 使用命令行工具运行 章节

jpackage

单独看 jpackage 可能不太好理解, 但是如果是顺着上文下来的, 那么自然而然就会 jpackage 打包了, 一脉相承

  • 首先通过 jpackage --help 查看选项列表说明, 官方已经给出了针对多种情况的简单命令行案例, 而且把各种参数已经做好了分类
  • 几个关键名词和参数
    • application package suitable: 就是安装程序包, 双击后是一套安装程序, 安装好后才能在指定位置找到 exe 运行程序
    • application image: 就是运行程序包, 双击直接运行, 无需安装的那种
    • –name: 打包出的 exe 的名称, 生成的存放 exe 的文件夹也是这个名称
    • –type: 打包的类型, Windows 平台支持 {“app-image”, “exe”, “msi”}, 只有 app-image 是打包为可执行程序, 其他都是安装包程序
    • –input: 指定一个资源文件夹, 该文件夹下的所有内容都会被打包
      • 我目前只有在打包非模块化工程时, 通过该参数指定 jar 包所在的路径, 目录下也只有一个可运行的 jar 包, 暂没感觉到其他用处
    • –dest: 生成物的输出路径, 默认是当前所在工作路径, 该路径与 --input 指定的路径 一定不能相同, 会生成超深文件夹, 无法直接删除(可递归从里向外删除)
    • –icon: 指定可执行程序的图标
    • –module-path: 默认包含 JDK JMods 目录, 额外补充依赖模块的查找路径, 可以是模块的目录, 也可以是模块化的 jar 的路径, 用 ; 分割
    • –module: 指定应用程序的主模块(和主类), 必须在模块查找路径所覆盖的范围内, 一旦指定该选项, 主模块将会链接到运行时?? 我觉得应该是自动生成合适的 JRE 的意思
    • –main-jar:
      • 非模块化的 Jar 打包为 exe 时需要该参数, 同时 --input 参数必须存在, 且该 jar 必须在 --input 指定的目录下
      • 模块化的 Jar 也可以用该参数打包 exe, 但是没必要, 直接用编译好的模块 class 打包就好了, 无需转成 Jar 再打包. 我猜的
    • –main-class
      • 如果 jar 是可直接运行的 jar, 则可以不使用该参数指定运行的主类. 也可以使用该参数将运行主类指向其他可运行类
      • 如果 jar 是不可直接运行的, 但是包含可运行主类, 可通过该参数指定
    • 其他 win 参数: … 自行查看 help 的解释

wix v3

在这里插入图片描述

使用 jpackage 需要安装 WIX v3 或更高版本, 官网, GitHub, 安装完貌似不需要配置环境变量即可直接使用

Modular JavaFX Project (非 Maven / Gradle)

准备

# 设置环境变量
set PATH_TO_FX_MODS="C:\mrathena\develop\javafx-jmods-20.0.1"

# 切换工作路径设置到本工程内
cd C:\mrathena\develop\workspace\idea\mrathena\code.study\javafx

# 编译
dir /s /b src\*.java > sources.txt & javac --module-path %PATH_TO_FX% -d mods/javafx @sources.txt & del sources.txt

打包为安装程序

jpackage --name javafx.demo --module-path %PATH_TO_FX_MODS%;mods --module com.mrathena/com.mrathena.HelloWorld

生成一个 javafx.demo-1.0.exe 的安装程序, 安装后默认在 C:\Program Files 下, 双击 javafx.demo.exe 可运行

在这里插入图片描述
在这里插入图片描述

在 控制面板-卸载程序 中可以看到安装的该程序, 可正常卸载

在这里插入图片描述

打包为运行程序

# 自动生成 JRE, 输出到当前目录
jpackage --type app-image --name javafx.demo --module-path %PATH_TO_FX_MODS%;mods --module com.mrathena/com.mrathena.HelloWorld
# 使用自定义的 JRE, 输出到当前目录
jpackage --type app-image --name javafx.demo --module-path %PATH_TO_FX_MODS%;mods --module com.mrathena/com.mrathena.HelloWorld --runtime-image jre
# 自动生成 JRE, 自定义图标, 输出到桌面
jpackage --type app-image --name javafx.demo --icon D:\resource\头像.狐狸.ico --dest C:\Users\mrathena\Desktop --module-path %PATH_TO_FX_MODS%;mods --module com.mrathena/com.mrathena.HelloWorld

测试发现, 自动生成的 JRE 比我自定义的 JRE 体积更小, 0.0

在这里插入图片描述

双击 javafx.demo.exe 即可直接运行

补充说明

可以使用 IntelliJ IDEA 编译的结果来生成可执行程序, IDEA 默认将 class 文件生成在 out\production\项目名 目录下, 修改打包命令如下

在这里插入图片描述

# 修改 --module-path 补充的 mods 路径为 out\production 即可直接使用 IDEA 的编译结果
jpackage --type app-image --name javafx.demo --dest C:\Users\mrathena\Desktop --module-path %PATH_TO_FX_MODS%;out\production --module com.mrathena/com.mrathena.HelloWorld --icon D:\resource\头像.狐狸.ico

Modular JavaFX Maven Project

准备

# 设置环境变量
set PATH_TO_FX_MODS="C:\mrathena\develop\javafx-jmods-20.0.1"

# 切换工作路径设置到本工程内
cd C:\mrathena\develop\workspace\idea\mrathena\code.study\java.javafx.starter

# 编译, 可以使用 IDEA Maven JavaFX 插件提供的 javafx:run 来编译
mvn clean javafx:run

打包为运行程序

同理, 生成的编译文件在 target\classes 目录, 将 target 目录补充到 --module-path

# 理论上通过下述命令可以直接打包了, 虽然成功打包了, 但是打包报了下面的错
# java.lang.IllegalArgumentException: "版本 [1.0-SNAPSHOT] 包含无效组件 [0-SNAPSHOT]"
jpackage --type app-image --name javafx.demo --icon D:\resource\头像.狐狸.ico --dest C:\Users\mrathena\Desktop --module-path %PATH_TO_FX_MODS%;target --module com.mrathena.javafx/com.mrathena.javafx.HelloApplication

# 添加 -app-version 参数后可以运行了
jpackage --type app-image --name javafx.demo --icon D:\resource\头像.狐狸.ico --dest C:\Users\mrathena\Desktop --module-path %PATH_TO_FX_MODS%;target --module com.mrathena.javafx/com.mrathena.javafx.HelloApplication --app-version 1.0.0

在这里插入图片描述

虽然可以成功打包与运行, 但是我还没理解为什么能读到 pom.xml 里面的 version, 然后报这个错

打包为安装程序

jpackage --name javafx.demo --icon D:\resource\头像.狐狸.ico --dest C:\Users\mrathena\Desktop --module-path %PATH_TO_FX_MODS%;target --module com.mrathena.javafx/com.mrathena.javafx.HelloApplication --app-version 1.0.0

和 打包为运行程序 一样, --type 参数, 使用默认值 exe 就好了

Non-Modular Java Project

要先把非模块化工程打包成一个可以运行的 Fat Jar, 这里以以前的一个 非模块化的 swing 工程为例, 打出的 jar 是 qq.speed.local.data.manager.jar, 确保通过 java -jar qq.speed.local.data.manager.jar 可直接运行

通过 IDEA 将工程打包为可运行 Jar

在这里插入图片描述

在 项目结构 里添加 Jar Artifact, 选择正确的 Main Class, 默认勾选 Extract to the target JAR 以便于生成无依赖的 Fat Jar. 其他同样默认即可. 然后在 [Build - Build Artifacts …] 选项里 [build] 该 Jar Artifact, 就会在 out\artifacts 目录下生成可直接运行的 Jar

将可运行 Jar 打包为运行程序

非模块化工程打包时不需要使用模块化工程专用的那些参数, 确保该 jar 存在于 --input 指定的目录中

# 切换到 jar 所在的目录
# cd C:\mrathena\develop\workspace\idea\mrathena\code.project\qq.speed.local.data.manager\out\artifacts\qq_speed_local_data_manager_jar
# 打包
# 因为该 jar 可以直接运行, 即 jar 知道主类是哪个, 所以打包时可以不指定 --main-class, 当然指定也可以, 甚至可以强行指定为其他可运行主类
jpackage --type app-image --icon D:\resource\头像.狐狸.ico --input . --dest C:\Users\mrathena\Desktop --name qq.speed.local.data.manager --main-jar qq.speed.local.data.manager.jar
jpackage --type app-image --icon D:\resource\头像.狐狸.ico --input . --dest C:\Users\mrathena\Desktop --name qq.speed.local.data.manager --main-jar qq.speed.local.data.manager.jar --main-class com.mrathena.QQSpeedLocalDataManager
jpackage --type app-image --icon D:\resource\头像.狐狸.ico --input . --dest C:\Users\mrathena\Desktop --name qq.speed.local.data.manager --main-jar qq.speed.local.data.manager.jar --main-class com.mrathena.tool.QQGroupKit

有一点非常重要, 就是默认输出目录就是当前目录, 该目录一定不能和参数 --input 指定的打包目录相同(该目录下的所有文件都会被打包), 建议显式指定 --desc 参数并指向不同位置. 不然生成出的内容会不断被尝试打包, 最终因为文件夹深度太深而报错或路径超过字符数限制而报错, 关键还无法直接删除, 需要写代码递归从最深处往外删, 我不小心生成了一个 2000 多层的文件夹目录, 最终靠写递归代码才删掉

后来发现 [Build - Build Artifacts ... - 选择 Jar Artifact - clean] 可以非常方便的删除 out\artifacts 下的所有文件

在这里插入图片描述
在这里插入图片描述

双击 qq.speed.local.data.manager.exe 即可直接运行

就是 jar 包没有打在 exe 中, 处于暴露状态, 使用 exe4j 可以把 jar 一起打到 exe 中

将可运行 Jar 打包为运行程序

jpackage --icon D:\resource\头像.狐狸.ico --input . --dest C:\Users\mrathena\Desktop --name qq.speed.local.data.manager --main-jar qq.speed.local.data.manager.jar

和 打包为运行程序 一样, --type 参数, 使用默认值 exe 就好了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/467747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像质量评价指标FID、LPIPS、NIQE及其代码

文章目录 FIDLPIPSNIQE FID FID的全称是Frchet Inception Distance&#xff0c;用于衡量两个多元正态分布的距离&#xff0c;数值越小越好。具体的&#xff0c;FID使用Inception Net-V3全连接前的2048维向量作为图片的特征向量&#xff0c;再计算两张图像特征之间的距离。 F I…

医疗行业数据库老牌厂商 InterSystems 的新实践

作者 | 宋慧 出品 | CSDN 云计算 数据赛道火爆&#xff0c;呈现了爆发式增长&#xff0c;CSDN 的《新程序员》杂志曾做过主题为“新数据库时代”的系列专题报道&#xff0c;而且从 Gartner2022 年魔力象限也能看到&#xff0c;仍然有众多厂商汇集在云数据库这个领域的象限中。…

使用脚手架新建Vue项目

1.安装阿里云镜像仓库 npm config get registry 然后我们进行安装vue的客户端&#xff0c;npm install -g vue/cli 然后我们可以进行查看版本输入vue --version 然后我们到目录下面新建一个vue项目 vue create 项目名称&#xff08;选择的时候选择Vue2版本&#xff09; 这是第…

C++文件读写类介绍

一、现有的文件读写方案 方案一&#xff1a;采用C标准库读写 该库拥有输入输出模板类及两个标准实例化集&#xff1a;一个是用于操作char类型元素的实例化集(即常用的cin&#xff0c;cout等)&#xff0c;另一个用于操作wchar_t类型元素的实例化集。 模板类以basic_作为前缀&…

CentOS----本地YUM源配置

1.cd /etc/yum.repos.d 2. cp -p CentOS-Media.repo M.repo mkdir yuan mv CentOS-* yuan 3. vi M.repo (修改配置文件内容如下,/mnt/cdrom 为yum源目录) 4.创建挂载目录 和 上面配置文件中file:///mnt/cdrom 一致 mkdir -p /mnt/cdrom 5.将本地yum 挂载到刚创建的文件…

【思考】聊聊低代码的实践之路

文章目录 背景一、最初的疑惑二、简单聊聊原理三、组织内实践案例四、实践带来的反思五、最后聊几句问题 背景 这个概念由来已久&#xff0c;但是在国内兴起&#xff0c;是最近几年&#xff1b; 低代码即Low-Code&#xff1b; 指提供可视化开发环境&#xff0c;可以用来创建和…

(一)Kubernetes - 介绍

Kubernetes介绍 1. 介绍1.1 什么是Kubernetes1.2 K8s主要功能1.3 K8s架构1.4 K8S核心概念1.5 完整流程 2. K8S安装方式选择2.1 kubeadm2.2 手动部署(二进制)2.3 Rancher2.4 kubespray 3. 思维导图 1. 介绍 1.1 什么是Kubernetes ​ Kubernetes是Google公司在2014年6月开源的一…

根据cadence设计图学习硬件知识day06 了解一些电源转化芯片和 稳压器 和 开关芯片

1. TPL920 (高精度线性稳压器) 1.1.TPL920 介绍 TPL920系列产品是2A大电流、6μVRMS低噪声、高PSRR、高精度线性稳压器&#xff0c;通常具有在2A负载条件下的110 mV超低电压降。这TPL920系列产品同时支持固定输出电压范围从0.8伏到3.95伏&#xff0c;输出电压可调范围为0.8V至…

d2l BERT预训练(model+dataset*+train)

千呼万唤始出来&#xff0c;终于来到了bert。本篇博客先介绍预训练部分&#xff0c;dataset部分只介绍简洁输入输出&#xff0c;详细的另行更新新的blog。 目录 1.model 1.1bert总述 1.2输入表示 1.3Encoder 1.3.1验证输出 1.4掩敝语言模型mlm 1.4.1forward探索 Layer…

有什么台灯性价比高又实惠的品牌?护眼台灯性价比高的led大灯

不管你处在学生被动学习还是上班后主动学习的阶段&#xff0c;为自己挑选一款合适的台灯非常重要&#xff0c;因为夜晚的氛围能达到很高的学习效率&#xff0c;而台灯可以保证我们有一个舒适的阅读感受。那在为学习需求挑选台灯时&#xff0c;不应该以平价作为选购标准&#xf…

FA-PEG-Silane 叶酸-聚乙二醇-硅烷 Silane-PEG2000-FA,PEG分子量2000

FA-PEG-Silane 叶酸-聚乙二醇-硅烷 中文名称&#xff1a;叶酸聚乙二醇硅烷 英文名称&#xff1a;FA-PEG-Silane&#xff0c;Folic acid PEG Silane 性状&#xff1a;液体或者固体&#xff0c;取决于分子量 溶剂&#xff1a;溶于水、DMSO、DMF等常规性有机溶剂 分子量&…

第十二讲 常用数据结构之集合

在学习了列表和元组之后&#xff0c;我们再来学习一种容器型的数据类型&#xff0c;它的名字叫集合&#xff08;set&#xff09;。说到集合这个词大家一定不会陌生&#xff0c;在数学课本上就有这个概念。如果我们把一定范围的、确定的、可以区别的事物当作一个整体来看待&…

三维可视化智慧档案馆之八防环境监控系统平台白皮书

目录 一、智慧档案馆建设目的 二、智慧档案馆集成度 三、智慧档案馆架构 3.1库房环境监测 3.2库房安防监控 四、智慧档案馆功能简介 4.1档案室一体化控制管理系统建设方案 4.2温湿度检测建设方案 4.3恒温控制建设方案 4.4烟雾感应检测系统 4.5安防系统建设…

STM32开发(十七)STM32F103 片内资源 —— 独立看门狗 IWDG 详解

文章目录 一、基础知识点二、开发环境三、STM32CubeMX相关配置四、Vscode代码讲解五、结果演示 一、基础知识点 STM32F10xxx内置两个看门狗&#xff0c;提供了更高的安全性、时间的精确性和使用的灵活性。两个看门狗设备(独立看门狗和窗口看门狗)可用来检测和解决由软件错误引…

数据库做实验过程-------pyqt环境的配置

首先下载anacunda Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 找到windows最新版x86 64版本等待下载 双击运行安装包 此时一定要记录文件夹的位置&#xff0c;便于以后环境变量的配置。 别看是4.7但是以后可能会增加新的配置&…

学生管理系统

一、项目框架 二、 CommandInfo.cs: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data.Common; //using MySql.Data.MySqlClient;namespace WinStudent {public class CommandInf…

JMU Oracle 实验五

问的问题 看了一下log和logfile&#xff0c;就是重做日志组和日志文件的地址信息看了归档的地址如何执行归档&#xff0c;就是switch那个语句 1. 查询Oracle数据库当前使用的联机重做日志文件组及成员信息 v$log&#xff1a;记录有关重做日志文件组相关的信息。 v$logfile&a…

数据结构——栈的构建

在本次的博客当中我们来向大家介绍两个看似很新没有听过&#xff0c;实际上我们之前已经实现过了的数据结构——栈和队列。 &#x1f335;栈 实质上栈就是一个具有特殊要求的线性表。栈在定义上要求我们只能从一端插入和一段删除数据。举一个简单的例子&#xff1a;我们一次向栈…

MySQL的ID用完了,怎么办?

目 录 一 首先首先分情况 二 自增ID 1 mysql 数据库创建一个自增键的表 2 导出表结构 3 重新创建 自增键是4294967295的表 4 查看表结构 5 异常测试 三 填充主键 1 首先创建一个test 表&#xff0c;主键不自增 2 插入主键最大值 3 再次插入主键最大值1 四 没有声明…

SSO、CAS、OAuth、OIDC

参考 简单了解概念&#xff1a; https://www.bilibili.com/video/BV1XG411w7DN/简单了解操作&#xff1a; https://www.bilibili.com/video/BV1334y11739/ openid-connect&#x1f44d;流程图解&#xff1a; https://www.youtube.com/watch?vt18YB3xDfXI &#xff08;图&#…