├── src ├── main │ ├── resources │ │ ├── LanguageResource.properties │ │ ├── LanguageResource_en.properties │ │ ├── LanguageResource_zh_CN.properties │ │ ├── META-INF │ │ │ └── native-image │ │ │ │ ├── proxy-config.json │ │ │ │ ├── predefined-classes-config.json │ │ │ │ ├── serialization-config.json │ │ │ │ ├── filter-file.json │ │ │ │ ├── jni-config.json │ │ │ │ ├── resource-config.json │ │ │ │ └── reflect-config.json │ │ ├── application.icns │ │ ├── application.ico │ │ ├── application.png │ │ └── logback.xml │ └── java │ │ ├── module-info.java │ │ └── com │ │ └── icuxika │ │ ├── i18n │ │ └── ObservableResourceBundleFactory.java │ │ ├── AppResource.java │ │ └── MainApp.java └── windows │ └── assets │ └── icon.ico ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── command └── resourceDir │ └── zh_CN.wxl ├── settings.gradle ├── .github └── workflows │ ├── macos.yml │ ├── ubuntu.yml │ └── windows.yml ├── README.en-US.md ├── gradlew.bat ├── Windows下遇到编码问题可以看看.md ├── README.md ├── gradlew ├── Windows下手动构建的演示.md └── pom.xml /src/main/resources/LanguageResource.properties: -------------------------------------------------------------------------------- 1 | title=title -------------------------------------------------------------------------------- /src/main/resources/LanguageResource_en.properties: -------------------------------------------------------------------------------- 1 | title=title -------------------------------------------------------------------------------- /src/main/resources/LanguageResource_zh_CN.properties: -------------------------------------------------------------------------------- 1 | title=标题 -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/proxy-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | build/ 3 | out/ 4 | target/ 5 | *.iml 6 | /.gradle/ 7 | /logs 8 | -------------------------------------------------------------------------------- /src/windows/assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaFX-Starter/JavaFX-Package-Sample/HEAD/src/windows/assets/icon.ico -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaFX-Starter/JavaFX-Package-Sample/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/application.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaFX-Starter/JavaFX-Package-Sample/HEAD/src/main/resources/application.icns -------------------------------------------------------------------------------- /src/main/resources/application.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaFX-Starter/JavaFX-Package-Sample/HEAD/src/main/resources/application.ico -------------------------------------------------------------------------------- /src/main/resources/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaFX-Starter/JavaFX-Package-Sample/HEAD/src/main/resources/application.png -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/predefined-classes-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type":"agent-extracted", 4 | "classes":[ 5 | ] 6 | } 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/serialization-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "types":[ 3 | ], 4 | "lambdaCapturingTypes":[ 5 | ], 6 | "proxies":[ 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /command/resourceDir/zh_CN.wxl: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | url "https://nexus.gluonhq.com/nexus/content/repositories/releases" 5 | } 6 | 7 | gradlePluginPortal() 8 | } 9 | } 10 | rootProject.name = 'JavaFX-Package-Sample' 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module sample { 2 | 3 | requires javafx.base; 4 | requires javafx.controls; 5 | requires javafx.fxml; 6 | requires javafx.swing; 7 | requires javafx.graphics; 8 | requires javafx.web; 9 | 10 | requires MaterialFX; 11 | requires org.slf4j; 12 | 13 | exports com.icuxika; 14 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/filter-file.json: -------------------------------------------------------------------------------- 1 | { "rules": [ 2 | {"excludeClasses" : "com.sun.glass.ui.mac.*"}, 3 | {"excludeClasses" : "com.sun.glass.ui.gtk.*"}, 4 | {"excludeClasses" : "com.sun.glass.ui.win.*"}, 5 | {"excludeClasses" : "com.sun.prism.es2.*"}, 6 | {"excludeClasses" : "com.sun.prism.d3d.*"}, 7 | {"excludeClasses" : "com.sun.scenario.effect.impl.es2.*"}, 8 | {"excludeClasses" : "com.sun.scenario.effect.impl.hw.d3d.*"}, 9 | {"excludeClasses" : "com.gluonhq.attach.**"} 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: Build installer for macOS 2 | on: 3 | push: 4 | branches: 5 | - java21 6 | 7 | jobs: 8 | build-app: 9 | strategy: 10 | matrix: 11 | os: [ macos-12 ] 12 | name: Build the application 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: checkout 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup Java 19 | uses: actions/setup-java@v3 20 | with: 21 | distribution: 'temurin' 22 | java-version: 21 23 | 24 | - name: chmod gradlew 25 | run: chmod +x ./gradlew 26 | 27 | - run: ./gradlew jpackage 28 | - run: mkdir staging && rm -rf build/build-package/JavaFXSample.app && cp build/build-package/*.dmg staging 29 | 30 | - name: get current time 31 | uses: 1466587594/get-current-time@v2 32 | id: current-time 33 | with: 34 | format: YYYYMMDD-HH 35 | utcOffset: "+08:00" 36 | 37 | - name: upload build result 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: JavaFXSample-${{ steps.current-time.outputs.formattedTime }}-${{ matrix.os }} 41 | path: staging 42 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Build installer for Linux 2 | on: 3 | push: 4 | branches: 5 | - java21 6 | 7 | jobs: 8 | build-app: 9 | strategy: 10 | matrix: 11 | os: [ ubuntu-20.04 ] 12 | name: Build the application 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: checkout 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup Java 19 | uses: actions/setup-java@v3 20 | with: 21 | distribution: 'temurin' 22 | java-version: 21 23 | 24 | - name: chmod gradlew 25 | run: chmod +x ./gradlew 26 | 27 | - run: ./gradlew jpackage 28 | - run: mkdir staging && rm -rf build/build-package/JavaFXSample && cp build/build-package/* staging 29 | 30 | - name: get current time 31 | uses: 1466587594/get-current-time@v2 32 | id: current-time 33 | with: 34 | format: YYYYMMDD-HH 35 | utcOffset: "+08:00" 36 | 37 | - name: upload build result 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: JavaFXSample-${{ steps.current-time.outputs.formattedTime }}-${{ matrix.os }} 41 | path: staging 42 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %highlight(%-5level) %boldGreen(%d{yyyy-MM-dd HH:mm:ss.SSS}) %boldYellow([%thread]) %cyan([%F:%L]) - %highlight(%msg%n) 6 | 7 | 8 | 9 | 10 | 11 | logs/application.log 12 | 13 | INFO 14 | 15 | 16 | logs/rolling/application.%d{yyyy-MM-dd}.log 17 | 30 18 | 3GB 19 | 20 | 21 | %-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%F:%L] %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/icuxika/i18n/ObservableResourceBundleFactory.java: -------------------------------------------------------------------------------- 1 | package com.icuxika.i18n; 2 | 3 | import javafx.beans.binding.StringBinding; 4 | import javafx.beans.property.ObjectProperty; 5 | import javafx.beans.property.SimpleObjectProperty; 6 | 7 | import java.util.ResourceBundle; 8 | 9 | /** 10 | * 多国语言属性绑定 11 | */ 12 | public class ObservableResourceBundleFactory { 13 | 14 | private final ObjectProperty resourceBundleProperty = new SimpleObjectProperty<>(); 15 | 16 | public ObjectProperty getResourceBundleProperty() { 17 | return resourceBundleProperty; 18 | } 19 | 20 | public ResourceBundle getResourceBundle() { 21 | return getResourceBundleProperty().get(); 22 | } 23 | 24 | public void setResourceBundle(ResourceBundle resourceBundle) { 25 | getResourceBundleProperty().set(resourceBundle); 26 | } 27 | 28 | public StringBinding getStringBinding(String key) { 29 | return new StringBinding() { 30 | { 31 | bind(resourceBundleProperty); 32 | } 33 | 34 | @Override 35 | protected String computeValue() { 36 | return getResourceBundle().getString(key); 37 | } 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Build installer for Windows 2 | on: 3 | push: 4 | branches: 5 | - java21 6 | 7 | jobs: 8 | build-app: 9 | strategy: 10 | matrix: 11 | os: [ windows-2019 ] 12 | name: Build the application 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - name: checkout 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup Java 19 | uses: actions/setup-java@v3 20 | with: 21 | distribution: 'temurin' 22 | java-version: 21 23 | 24 | - run: $Env:Path += ";C:\Program Files (x86)\WiX Toolset v3.11\bin" 25 | 26 | - name: build 27 | run: .\gradlew.bat jpackage 28 | shell: pwsh 29 | 30 | - name: copy 31 | run: New-Item -Name "staging" -ItemType "directory"; Copy-Item ".\build\build-package\*.msi" -Destination "staging" 32 | shell: pwsh 33 | 34 | - name: get current time 35 | uses: 1466587594/get-current-time@v2 36 | id: current-time 37 | with: 38 | format: YYYYMMDD-HH 39 | utcOffset: "+08:00" 40 | 41 | - name: upload build result 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: JavaFXSample-${{ steps.current-time.outputs.formattedTime }}-${{ matrix.os }} 45 | path: staging 46 | -------------------------------------------------------------------------------- /README.en-US.md: -------------------------------------------------------------------------------- 1 |

JavaFX-Package-Sample

2 |

JavaFX Project Packaging Example

3 |

English | 中文

4 | 5 | ## Branches 6 | - `java21` Package a `modular` JavaFX project using `Maven`, `Gradle`, and `GraalVM`. 7 | - `java21-non-modular` [Link](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java21-non-modular) Package a `non-modular` JavaFX project using `Maven`. 8 | - `java22-win32` [Link](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java22-win32) A JavaFX project that implements Windows global keyboard event listening by utilizing the `Foreign Function and Memory (FFM) API`. 9 | 10 | ## Requirements 11 | PowerShell: 12 | ``` 13 | $env:JAVA_HOME = "C:\CommandLineTools\Java\jdk-21\" 14 | ``` 15 | ``` 16 | $env:GRAALVM_HOME = "C:\CommandLineTools\Java\graalvm-jdk-21.0.2+13.1\" 17 | ``` 18 | 19 | ## Getting Started 20 | Gradle: 21 | ``` 22 | .\gradlew.bat jpackageImage 23 | .\gradlew.bat jpackage 24 | ``` 25 | 26 | Maven: 27 | ``` 28 | mvn clean 29 | mvn package 30 | mvn exec:exec@image 31 | mvn exec:exec@installer 32 | ``` 33 | 34 | GraalVM: 35 | ``` 36 | .\gradlew.bat nativeRunAgent 37 | .\gradlew.bat nativeBuild 38 | .\gradlew.bat nativeRun 39 | ``` 40 | ``` 41 | mvn -Pwin gluonfx:build 42 | mvn -Pwin gluonfx:run 43 | 44 | mvn -Pmac gluonfx:build 45 | mvn -Pmac gluonfx:run 46 | ``` -------------------------------------------------------------------------------- /src/main/java/com/icuxika/AppResource.java: -------------------------------------------------------------------------------- 1 | package com.icuxika; 2 | 3 | import com.icuxika.i18n.ObservableResourceBundleFactory; 4 | import javafx.beans.binding.StringBinding; 5 | import javafx.beans.property.ObjectProperty; 6 | import javafx.beans.property.SimpleObjectProperty; 7 | 8 | import java.net.URL; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.Locale; 12 | import java.util.ResourceBundle; 13 | 14 | public class AppResource { 15 | 16 | /** 17 | * 默认语言文件 Base Name 18 | */ 19 | private static final String LANGUAGE_RESOURCE_NAME = "LanguageResource"; 20 | 21 | /** 22 | * 语言资源工厂 23 | */ 24 | private static final ObservableResourceBundleFactory LANGUAGE_RESOURCE_FACTORY = new ObservableResourceBundleFactory(); 25 | 26 | /** 27 | * 支持的语言集合,应与语言资源文件同步手动更新 28 | */ 29 | public static final List SUPPORT_LANGUAGE_LIST = Arrays.asList(Locale.SIMPLIFIED_CHINESE, Locale.ENGLISH); 30 | 31 | /** 32 | * 记录当前所选时区 33 | */ 34 | private static final ObjectProperty currentLocale = new SimpleObjectProperty<>(); 35 | 36 | public static ObjectProperty currentLocaleProperty() { 37 | return currentLocale; 38 | } 39 | 40 | public static void setCurrentLocale(Locale locale) { 41 | currentLocaleProperty().set(locale); 42 | } 43 | 44 | /** 45 | * 更换语言的组件使用此方法初始化自己的值,调用 {@link AppResource#setLanguage(Locale)} 来更新界面语言 46 | * 47 | * @return 当前界面语言 48 | */ 49 | public static Locale getCurrentLocale() { 50 | return currentLocaleProperty().get(); 51 | } 52 | 53 | /** 54 | * 更新界面语言 55 | * 56 | * @param locale 区域 57 | */ 58 | public static void setLanguage(Locale locale) { 59 | setCurrentLocale(locale); 60 | LANGUAGE_RESOURCE_FACTORY.setResourceBundle(ResourceBundle.getBundle(LANGUAGE_RESOURCE_NAME, locale)); 61 | } 62 | 63 | /** 64 | * 获取指定标识的字符串绑定 65 | * 66 | * @param key 标识 67 | * @return 对应该标识的字符串属性绑定 68 | */ 69 | public static StringBinding getLanguageBinding(String key) { 70 | return LANGUAGE_RESOURCE_FACTORY.getStringBinding(key); 71 | } 72 | 73 | /** 74 | * 有此类所在路径决定相对路径基于/com/icuxika 75 | * 76 | * @param path 资源文件相对路径 77 | * @return 资源文件路径 78 | */ 79 | public static URL load(String path) { 80 | return AppResource.class.getResource(path); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/jni-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"[Lcom.sun.glass.ui.Screen;" 4 | }, 5 | { 6 | "name":"com.sun.glass.ui.Cursor", 7 | "methods":[{"name":"getNativeCursor","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] 8 | }, 9 | { 10 | "name":"com.sun.glass.ui.Screen", 11 | "methods":[{"name":"","parameterTypes":["long","int","int","int","int","int","int","int","int","int","int","int","int","int","int","int","float","float","float","float"] }] 12 | }, 13 | { 14 | "name":"com.sun.glass.ui.Size", 15 | "methods":[{"name":"","parameterTypes":["int","int"] }] 16 | }, 17 | { 18 | "name":"com.sun.javafx.font.directwrite.DWRITE_GLYPH_METRICS", 19 | "fields":[{"name":"advanceHeight"}, {"name":"advanceWidth"}, {"name":"bottomSideBearing"}, {"name":"leftSideBearing"}, {"name":"rightSideBearing"}, {"name":"topSideBearing"}, {"name":"verticalOriginY"}], 20 | "methods":[{"name":"","parameterTypes":[] }] 21 | }, 22 | { 23 | "name":"com.sun.javafx.font.directwrite.DWRITE_GLYPH_RUN", 24 | "fields":[{"name":"advanceOffset"}, {"name":"ascenderOffset"}, {"name":"bidiLevel"}, {"name":"fontEmSize"}, {"name":"fontFace"}, {"name":"glyphAdvances"}, {"name":"glyphIndices"}, {"name":"isSideways"}], 25 | "methods":[{"name":"","parameterTypes":[] }] 26 | }, 27 | { 28 | "name":"com.sun.javafx.font.directwrite.DWRITE_MATRIX", 29 | "fields":[{"name":"dx"}, {"name":"dy"}, {"name":"m11"}, {"name":"m12"}, {"name":"m21"}, {"name":"m22"}], 30 | "methods":[{"name":"","parameterTypes":[] }] 31 | }, 32 | { 33 | "name":"com.sun.javafx.font.directwrite.RECT", 34 | "fields":[{"name":"bottom"}, {"name":"left"}, {"name":"right"}, {"name":"top"}], 35 | "methods":[{"name":"","parameterTypes":[] }] 36 | }, 37 | { 38 | "name":"com.sun.prism.impl.PrismSettings", 39 | "fields":[{"name":"disableD3D9Ex"}, {"name":"forceGPU"}, {"name":"isVsyncEnabled"}, {"name":"verbose"}] 40 | }, 41 | { 42 | "name":"java.lang.Class", 43 | "methods":[{"name":"forName","parameterTypes":["java.lang.String","boolean","java.lang.ClassLoader"] }] 44 | }, 45 | { 46 | "name":"java.lang.Runnable", 47 | "methods":[{"name":"run","parameterTypes":[] }] 48 | }, 49 | { 50 | "name":"java.lang.String", 51 | "methods":[{"name":"toLowerCase","parameterTypes":["java.util.Locale"] }] 52 | }, 53 | { 54 | "name":"java.util.ArrayList", 55 | "methods":[{"name":"","parameterTypes":["int"] }, {"name":"add","parameterTypes":["java.lang.Object"] }] 56 | }, 57 | { 58 | "name":"java.util.HashMap", 59 | "methods":[{"name":"containsKey","parameterTypes":["java.lang.Object"] }, {"name":"put","parameterTypes":["java.lang.Object","java.lang.Object"] }] 60 | } 61 | ] 62 | -------------------------------------------------------------------------------- /src/main/java/com/icuxika/MainApp.java: -------------------------------------------------------------------------------- 1 | package com.icuxika; 2 | 3 | import io.github.palexdev.materialfx.controls.MFXButton; 4 | import io.github.palexdev.materialfx.enums.ButtonType; 5 | import javafx.application.Application; 6 | import javafx.geometry.Insets; 7 | import javafx.geometry.Pos; 8 | import javafx.scene.Scene; 9 | import javafx.scene.control.Label; 10 | import javafx.scene.layout.Background; 11 | import javafx.scene.layout.BackgroundFill; 12 | import javafx.scene.layout.CornerRadii; 13 | import javafx.scene.layout.VBox; 14 | import javafx.scene.paint.Color; 15 | import javafx.stage.Stage; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import java.util.Locale; 20 | 21 | public class MainApp extends Application { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(MainApp.class); 24 | 25 | @Override 26 | public void start(Stage primaryStage) throws Exception { 27 | AppResource.setLanguage(Locale.SIMPLIFIED_CHINESE); 28 | 29 | Label label = new Label(); 30 | label.textProperty().bind(AppResource.currentLocaleProperty().asString().concat(": ").concat(AppResource.getLanguageBinding("title"))); 31 | 32 | MFXButton zhButton = new MFXButton("中文"); 33 | zhButton.setPrefSize(120, 40); 34 | zhButton.setButtonType(ButtonType.FLAT); 35 | zhButton.setTextFill(Color.WHITE); 36 | zhButton.setBackground(new Background(new BackgroundFill(Color.DODGERBLUE, new CornerRadii(4), Insets.EMPTY))); 37 | zhButton.setOnAction(event -> AppResource.setLanguage(Locale.SIMPLIFIED_CHINESE)); 38 | 39 | MFXButton enButton = new MFXButton("英文"); 40 | enButton.setPrefSize(120, 40); 41 | enButton.setButtonType(ButtonType.FLAT); 42 | enButton.setTextFill(Color.WHITE); 43 | enButton.setBackground(new Background(new BackgroundFill(Color.DODGERBLUE, new CornerRadii(4), Insets.EMPTY))); 44 | enButton.setOnAction(event -> AppResource.setLanguage(Locale.ENGLISH)); 45 | 46 | VBox vBox = new VBox(); 47 | vBox.setAlignment(Pos.CENTER); 48 | vBox.setSpacing(10); 49 | vBox.getChildren().addAll(label, zhButton, enButton); 50 | 51 | primaryStage.titleProperty().bind(AppResource.getLanguageBinding("title")); 52 | primaryStage.setScene(new Scene(vBox, 600, 400)); 53 | primaryStage.show(); 54 | 55 | LOGGER.trace("[trace]日志控制台输出"); 56 | LOGGER.debug("[debug]日志控制台输出"); 57 | LOGGER.info("[info]日志记录到logs/application.log中"); 58 | LOGGER.warn("[warn]日志记录到logs/application.log中"); 59 | LOGGER.error("[error]日志记录到logs/application.log中"); 60 | } 61 | 62 | public static void main(String[] args) { 63 | launch(args); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /Windows下遇到编码问题可以看看.md: -------------------------------------------------------------------------------- 1 | Windows下一些编码问题的记录 2 | ========== 3 | 4 | ## 添加代码 5 | 在`MainApp`的`start`方法末尾添加 6 | ``` 7 | System.out.println("你好"); 8 | ``` 9 | 10 | ## PowerShell 11 | 查看当前编码 12 | ```shell 13 | [System.Console]::OutputEncoding 14 | ``` 15 | ```shell 16 | BodyName : gb2312 17 | EncodingName : 简体中文(GB2312) 18 | HeaderName : gb2312 19 | WebName : gb2312 20 | WindowsCodePage : 936 21 | IsBrowserDisplay : True 22 | IsBrowserSave : True 23 | IsMailNewsDisplay : True 24 | IsMailNewsSave : True 25 | IsSingleByte : False 26 | EncoderFallback : System.Text.InternalEncoderBestFitFallback 27 | DecoderFallback : System.Text.InternalDecoderBestFitFallback 28 | IsReadOnly : False 29 | CodePage : 936 30 | ``` 31 | 32 | ### 1.使用`mvn javafx:run` 33 | > Java标准输出流(GB2312) -> PowerShell(GB2312) 34 | ```shell 35 | 你好 36 | ``` 37 | 38 | ### 2.使用`.\gradlew.bat run` 39 | > Java标准输出流(GB2312) -> gradle(UTF-8) -> PowerShell(GB2312) 40 | ```shell 41 | 锟斤拷锟? 42 | ``` 43 | ### 3.修改`build.gradle`中的`application`配置块为 44 | > 使标准输出流使用`UTF-8`编码 45 | ```shell 46 | application { 47 | applicationName = "JavaFXSample" 48 | mainModule.set("sample") 49 | mainClass.set("com.icuxika.MainApp") 50 | applicationDefaultJvmArgs = [ 51 | // ZGC 52 | "-XX:+UseZGC", 53 | // 当遇到空指针异常时显示更详细的信息 54 | "-XX:+ShowCodeDetailsInExceptionMessages", 55 | "-Dsun.java2d.opengl=true", 56 | // 不添加此参数,打包成exe后,https协议的网络图片资源无法加载 57 | "-Dhttps.protocols=TLSv1.1,TLSv1.2", 58 | "-Dsun.stdout.encoding=UTF-8", 59 | "-Dsun.stderr.encoding=UTF-8" 60 | ] 61 | } 62 | ``` 63 | ### 4.再次执行`.\gradlew.bat run` 64 | > Java标准输出流(UTF-8) -> gradle(UTF-8) -> PowerShell(GB2312) 65 | ```shell 66 | 浣犲ソ 67 | ``` 68 | 69 | ### 5.设置`PowerShell`使用`UTF-8`编码 70 | ```shell 71 | [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8 72 | ``` 73 | ```shell 74 | Preamble : 75 | BodyName : utf-8 76 | EncodingName : Unicode (UTF-8) 77 | HeaderName : utf-8 78 | WebName : utf-8 79 | WindowsCodePage : 1200 80 | IsBrowserDisplay : True 81 | IsBrowserSave : True 82 | IsMailNewsDisplay : True 83 | IsMailNewsSave : True 84 | IsSingleByte : False 85 | EncoderFallback : System.Text.EncoderReplacementFallback 86 | DecoderFallback : System.Text.DecoderReplacementFallback 87 | IsReadOnly : False 88 | CodePage : 65001 89 | ``` 90 | 91 | ### 6.使用`mvn javafx:run` 92 | > Java标准输出流(GB2312) -> PowerShell(UTF-8) 93 | ```shell 94 | ��� 95 | ``` 96 | 97 | ### 7.使用`.\gradlew.bat run` 98 | > Java标准输出流(UTF-8) -> PowerShell(UTF-8) 99 | ```shell 100 | 你好 101 | ``` 102 | 103 | ### 8.修改`build.gradle`中的`application`配置块为 104 | ```shell 105 | application { 106 | applicationName = "JavaFXSample" 107 | mainModule.set("sample") 108 | mainClass.set("com.icuxika.MainApp") 109 | applicationDefaultJvmArgs = [ 110 | // ZGC 111 | "-XX:+UseZGC", 112 | // 当遇到空指针异常时显示更详细的信息 113 | "-XX:+ShowCodeDetailsInExceptionMessages", 114 | "-Dsun.java2d.opengl=true", 115 | // 不添加此参数,打包成exe后,https协议的网络图片资源无法加载 116 | "-Dhttps.protocols=TLSv1.1,TLSv1.2" 117 | ] 118 | } 119 | ``` 120 | 121 | ### 9.再次执行`.\gradlew.bat run` 122 | > Java标准输出流(GB2312) -> gradle(UTF-8) -> PowerShell(UTF-8) 123 | > 124 | ```shell 125 | ��� 126 | ``` 127 | 128 | ### 总结 129 | - java 默认标准输出流使用的编码由操作系统的编码决定 130 | - maven 以`Maven plugin for JavaFX`插件为例,它实际获取到`java`和`jlink`的路径后通过`cmd /c java`的方式来执行命令,且插件没有提供参数让你传递`vm`选项,因此无法传递`sun.stdout.encoding`之类的属性给到插件里的命令,想要不乱码,只能使显示输出的前端编码匹配操作系统的默认编码。注意:其他的maven项目执行代码的`java`程序如果没有隔了这么一层,通过对`maven-compiler-plugin`设置``来设置相关选项 131 | - gradle 由于`gradle run`没有像`mvn javafx:run`这样隔了一层间接调用`java`可以通过`applicationDefaultJvmArgs`来设置选项。注意:`gradle jpackageImage`的情况就跟上面很像了 132 | - gradle 还有一处特别,gradle编译项目时,相关流会经历 gradle -> gradle daemon -> java compiler,java compiler -> gradle daemon -> gradle,其中 `gradle daemon -> gradle` 的时候,默认使用了`UTF-8`来进行序列化,所以gradle相关的命令注释多了中间一层,同时由于中间默认多了层`UTF-8`转化,且插件无法传入`vm`选项时,这个乱码无解,只能Windows操作系统全局设置`UTF-8` 133 | - 上面所说的`隔了一层`主要意思是指能不能传递`vm`选项给到`java`或`jlink` 134 | 135 | ### 补充 136 | 如果你在`PowerShell`中执行`[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8`后,发现`java --help`乱码了,你可以执行`[System.Console]::OutputEncoding = [System.Text.Encoding]::Default`回到默认设置,也可以 137 | ```shell 138 | C:\CommandLineTools\Java\jdk-21\bin\java.exe -D"sun.stdout.encoding"=UTF-8 -D"sun.stderr.encoding"=UTF-8 --help 139 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --help -J-D"sun.stdout.encoding"=UTF-8 -J-D"sun.stderr.encoding"=UTF-8 140 | ``` 141 | > 注意目标为`java`时,`-D`要在`--help`的前面,目标为`jpackage`时,`-J-D`可以放在后面 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

JavaFX-Package-Sample

2 |

JavaFX 打包示例(完结篇)

3 |

English | 中文

4 | 5 | ## 分支说明 6 | 7 | - `java21` 使用`Maven`,`Gradle`和`GraalVM`打包一个模块化的JavaFX项目。 8 | - `java21-non-modular` [链接](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java21-non-modular) 使用 9 | `Maven`打包一个非模块化的JavaFX项目。 10 | - `java22-win32` [链接](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java22-win32) 一个利用 11 | `Foreign Function and Memory (FFM) API`实现Windows全局键盘事件监听的JavaFX项目。 12 | 13 | ## HelloWorld [链接](https://github.com/icuxika/HelloWorld) 14 | 15 | 这个项目演示了如何完全使用`PowerShell7`来编译一个Java项目并创建可执行的jar包和可运行exe文件,项目存在包结构并引入第三方依赖。 16 | 17 | ## 环境设置 18 | #### `jpackage` 构建出安装包依赖的程序(如果只需要构建出可执行程序就可以跳过此配置) 19 | - Windows: 需要安装 [WIX TOOLSET](https://wixtoolset.org/) ,并将其bin目录添加到环境变量中 20 | - macOS/Linux: 缺失的包一般可以根据提示通过包管理器直接安装 21 | 22 | #### 在IDEA中构建项目 23 | - 项目结构 SDK 选择 JDK21 24 | - Gradle:Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> 指定 Gradle JVM 为项目 SDK 25 | 26 | #### 使用命令行(此处演示使用Windows的PowerShell,macOS和Linux假设你熟悉在命令行中设置临时环境变量) 27 | > `$env:xxx = "yyyy"`是设置一个临时环境变量,重启终端或者开启一些新的PowerShell标签页会失效 28 | 29 | - 设置`JAVA_HOME`环境变量 `$env:JAVA_HOME = "C:\CommandLineTools\Java\jdk-21\"` 30 | - > 使用 `gradlew`或者`mvn`的时候,只需要存在`JAVA_HOME`这个环境变量就可以,不需要`java`等命令配置在环境变量PATH中 31 | - 设置`GRAALVM_HOME`环境变量 `$env:GRAALVM_HOME = "C:\CommandLineTools\Java\graalvm-jdk-21+35.1"` 32 | - 设置`JAVA_TOOL_OPTIONS`环境变量可以为`gradle`设置代理 33 | `$env:JAVA_TOOL_OPTIONS = "-DsocksProxyHost=localhost -DsocksProxyPort=7890"` 34 | - 若要进行`GraalVM`构建,需要使用`Maven 3.8.8` 35 | 36 | ## 项目构建 37 | 38 | ### `jpackage` 39 | 40 | ##### gradle 41 | 42 | > 构建可执行程序 43 | 44 | ```shell 45 | .\gradlew.bat jpackageImage 46 | ``` 47 | 48 | > 构建安装包 49 | 50 | ```shell 51 | .\gradlew.bat jpackage 52 | ``` 53 | 54 | ##### maven 55 | 56 | 请根据不同操作系统修改`exec-maven-plugin`插件中有关下方两个命令的`--icon`的参数值,Windows为 57 | `${project.basedir}/src/main/resources/application.ico`,macOS为`${project.basedir}/src/main/resources/application.icns` 58 | ,Linux为`${project.basedir}/src/main/resources/application.png` 59 | > 运行(Maven plugin for JavaFX) 60 | 61 | ```shell 62 | mvn javafx:run 63 | ``` 64 | 65 | > 构建出`.bat`形式的可执行程序(Maven plugin for JavaFX) 66 | 67 | ```shell 68 | mvn javafx:jlink 69 | ``` 70 | 71 | > 构建可执行程序(手动拼接jpackage命令),`此命令构建出的结果其实与上面差别不大,但是是.exe的形式,并且图标已经设置好了` 72 | 73 | ```shell 74 | mvn clean 75 | mvn package 76 | mvn exec:exec@image 77 | ``` 78 | 79 | > 构建安装包(手动拼接jpackage命令) 80 | 81 | ```shell 82 | mvn clean 83 | mvn package 84 | mvn exec:exec@installer 85 | ``` 86 | 87 | ### `Oracle GraalVM` 88 | 89 | - 下载[Oracle GraalVM](https://www.graalvm.org/downloads/) 90 | - 请运行在由`Visual Studio`提供的命令行环境中,`Windows 终端`可以直接在标签页中打开`Developer PowerShell for VS 2022` 91 | ,其他用户可以在开始菜单的`Visual Studio 2022`文件夹中找到 92 | - 不关注 `module-info.java` 文件的存在与否 93 | 94 | #### JAVA_HOME和GRAALVM_HOME可以设置为相同值 95 | 96 | ```shell 97 | > $env:JAVA_HOME 98 | C:\CommandLineTools\Java\graalvm-jdk-21.0.2+13.1\ 99 | > $env:GRAALVM_HOME 100 | C:\CommandLineTools\Java\graalvm-jdk-21.0.2+13.1\ 101 | ``` 102 | 103 | #### 此项目使用的 Oracle GraalVM 版本 104 | 105 | ```shell 106 | java 21.0.2 2024-01-16 LTS 107 | Java(TM) SE Runtime Environment Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30) 108 | Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30, mixed mode, sharing) 109 | ``` 110 | 111 | ##### gradle 112 | 113 | ###### 构建与运行 114 | 115 | ```shell 116 | .\gradlew.bat nativeBuild 117 | .\gradlew.bat nativeRun 118 | ``` 119 | 120 | ###### `Downloading JavaFX static libs...`网络问题 121 | 122 | Gluon下载`JavaFX static libs` 123 | 涉及到的相关源码[FileOps.java](https://github.com/gluonhq/substrate/blob/master/src/main/java/com/gluonhq/substrate/util/FileOps.java) 124 | ,因此可以通过配置`java`的一些系统属性(需要梯子)来加速,在项目根目录下新建文件`gradle.properties` 125 | 内容如下,配置项的有关文档[Java Networking and Proxies](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html) 126 | ,gradle相关文档[Accessing the web through a proxy](https://docs.gradle.org/current/userguide/build_environment.html#ex-configuring-an-http-proxy-using-gradle-properties) 127 | 128 | ```properties 129 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 130 | systemProp.http.proxyHost=127.0.0.1 131 | systemProp.http.proxyPort=7890 132 | systemProp.https.proxyHost=127.0.0.1 133 | systemProp.https.proxyPort=7890 134 | ``` 135 | 136 | 重新执行相关命令,可能会因为之前的下载缓存导致重新执行命令失败,这时可以去删除此文件夹`C:\Users\你的用户名\.gluon\substrate` 137 | 再重新运行,`JavaFX static libs`下载成功后可以注释掉相关代理配置以防其他平台梯子配置不同,如果不想改配置文件,也可以通过下述命令的方式来使用代理 138 | 139 | ``` 140 | gradlew.bat nativeBuild -D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890 141 | ``` 142 | 143 | ##### maven 144 | 145 | ###### 构建与运行 146 | 147 | ```shell 148 | mvn -Pwin gluonfx:build 149 | mvn -Pwin gluonfx:run 150 | 151 | mvn -Pmac gluonfx:build 152 | mvn -Pmac gluonfx:run 153 | ``` 154 | 155 | ###### `Downloading JavaFX static libs...`网络问题 156 | 157 | ```shell 158 | mvn -Pwin gluonfx:build -D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890 159 | mvn -Pmac gluonfx:build -D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890 160 | ``` 161 | 162 | ## 日志 163 | 164 | 添加了支持GraalVM的Logback作为日志框架,在你自己的项目,如果是第一次添加以及后续修改了Logback配置文件的时候,在执行GraalVM构建之前,先用 165 | `mvn -Pwin gluonfx:runagent`或`.\gradlew.bat nativeRunAgent`来为`native-image`生成一些必要的文件,它会自动保存到 166 | `src/main/resources/META-INF/native-image`,然后在执行GraalVM构建,否则生成的程序执行时不会生成日志文件,正常情况下,会在程序执行目录创建 167 | `logs`目录及日志文件 168 | 169 | ## 补充 170 | 171 | 有时候`mvn gluonfx:build`或者`.\gradlew.bat jpackageImage`发生了错误后,执行`.\gradlew.bat clean`提示文件被占用,这时候可以打开 172 | `任务管理器-性能-资源监视器-CPU`,在`关联的句柄`处输入`gluon`、`jpackage`等关键词来检索出卡住的相关进程并干掉他 173 | 174 | ## Windows安装包制作 175 | `jpackage`默认使用`WiX Toolset`来制作安装包文件,可以通过命令行参数修改`WiX Toolset`相关配置文件,若不想使用`jpackage`内置安装包的制作工具,也可以在构建出image后自行制作安装包 176 | - [NSISBuilder](https://github.com/icuxika/NSISBuilder) 177 | - [WiXToolset3Builder](https://github.com/icuxika/WiXToolset3Builder) -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources":{ 3 | "includes":[{ 4 | "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" 5 | }, { 6 | "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" 7 | }, { 8 | "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" 9 | }, { 10 | "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E" 11 | }, { 12 | "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" 13 | }, { 14 | "pattern":"\\Qlogback-test.scmo\\E" 15 | }, { 16 | "pattern":"\\Qlogback-test.xml\\E" 17 | }, { 18 | "pattern":"\\Qlogback.scmo\\E" 19 | }, { 20 | "pattern":"\\Qlogback.xml\\E" 21 | }, { 22 | "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/ubidi.icu\\E" 23 | }, { 24 | "pattern":"javafx.controls:\\Qcom/sun/javafx/scene/control/skin/modena/modena.bss\\E" 25 | }, { 26 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-console-l1-1-0.dll\\E" 27 | }, { 28 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-console-l1-2-0.dll\\E" 29 | }, { 30 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-datetime-l1-1-0.dll\\E" 31 | }, { 32 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-debug-l1-1-0.dll\\E" 33 | }, { 34 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-errorhandling-l1-1-0.dll\\E" 35 | }, { 36 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-file-l1-1-0.dll\\E" 37 | }, { 38 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-file-l1-2-0.dll\\E" 39 | }, { 40 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-file-l2-1-0.dll\\E" 41 | }, { 42 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-handle-l1-1-0.dll\\E" 43 | }, { 44 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-heap-l1-1-0.dll\\E" 45 | }, { 46 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-interlocked-l1-1-0.dll\\E" 47 | }, { 48 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-libraryloader-l1-1-0.dll\\E" 49 | }, { 50 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-localization-l1-2-0.dll\\E" 51 | }, { 52 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-memory-l1-1-0.dll\\E" 53 | }, { 54 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-namedpipe-l1-1-0.dll\\E" 55 | }, { 56 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-processenvironment-l1-1-0.dll\\E" 57 | }, { 58 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-processthreads-l1-1-0.dll\\E" 59 | }, { 60 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-processthreads-l1-1-1.dll\\E" 61 | }, { 62 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-profile-l1-1-0.dll\\E" 63 | }, { 64 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-rtlsupport-l1-1-0.dll\\E" 65 | }, { 66 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-string-l1-1-0.dll\\E" 67 | }, { 68 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-synch-l1-1-0.dll\\E" 69 | }, { 70 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-synch-l1-2-0.dll\\E" 71 | }, { 72 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-sysinfo-l1-1-0.dll\\E" 73 | }, { 74 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-timezone-l1-1-0.dll\\E" 75 | }, { 76 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-util-l1-1-0.dll\\E" 77 | }, { 78 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-conio-l1-1-0.dll\\E" 79 | }, { 80 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-convert-l1-1-0.dll\\E" 81 | }, { 82 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-environment-l1-1-0.dll\\E" 83 | }, { 84 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-filesystem-l1-1-0.dll\\E" 85 | }, { 86 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-heap-l1-1-0.dll\\E" 87 | }, { 88 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-locale-l1-1-0.dll\\E" 89 | }, { 90 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-math-l1-1-0.dll\\E" 91 | }, { 92 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-multibyte-l1-1-0.dll\\E" 93 | }, { 94 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-private-l1-1-0.dll\\E" 95 | }, { 96 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-process-l1-1-0.dll\\E" 97 | }, { 98 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-runtime-l1-1-0.dll\\E" 99 | }, { 100 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-stdio-l1-1-0.dll\\E" 101 | }, { 102 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-string-l1-1-0.dll\\E" 103 | }, { 104 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-time-l1-1-0.dll\\E" 105 | }, { 106 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-utility-l1-1-0.dll\\E" 107 | }, { 108 | "pattern":"javafx.graphics:\\Qcom/sun/glass/utils/NativeLibLoader.class\\E" 109 | }, { 110 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/FillCircle_Color.obj\\E" 111 | }, { 112 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/FillPgram_Color.obj\\E" 113 | }, { 114 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_Color.obj\\E" 115 | }, { 116 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_TextureFirstPassLCD.obj\\E" 117 | }, { 118 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_TextureRGB.obj\\E" 119 | }, { 120 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_TextureSecondPassLCD.obj\\E" 121 | }, { 122 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Texture_Color.obj\\E" 123 | }, { 124 | "pattern":"javafx.graphics:\\Qglass.dll\\E" 125 | }, { 126 | "pattern":"javafx.graphics:\\Qjavafx_font.dll\\E" 127 | }, { 128 | "pattern":"javafx.graphics:\\Qmsvcp140.dll\\E" 129 | }, { 130 | "pattern":"javafx.graphics:\\Qmsvcp140_1.dll\\E" 131 | }, { 132 | "pattern":"javafx.graphics:\\Qmsvcp140_2.dll\\E" 133 | }, { 134 | "pattern":"javafx.graphics:\\Qprism_d3d.dll\\E" 135 | }, { 136 | "pattern":"javafx.graphics:\\Qucrtbase.dll\\E" 137 | }, { 138 | "pattern":"javafx.graphics:\\Qvcruntime140.dll\\E" 139 | }, { 140 | "pattern":"javafx.graphics:\\Qvcruntime140_1.dll\\E" 141 | }]}, 142 | "bundles":[{ 143 | "name":"LanguageResource", 144 | "locales":["", "en", "zh-CN"] 145 | }, { 146 | "name":"com.sun.javafx.tk.quantum.QuantumMessagesBundle", 147 | "locales":["", "zh-CN"] 148 | }, { 149 | "name":"com/sun/glass/ui/win/themes", 150 | "locales":["", "zh-CN"] 151 | }] 152 | } 153 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /Windows下手动构建的演示.md: -------------------------------------------------------------------------------- 1 | 使用`jpackage`命令在Windows平台手动构建可执行程序和安装程序 2 | ========== 3 | 4 | ## 生成项目`jar`包(无法简单的通过`java -jar`执行) 5 | `gradle` -> `.\build\libs\JavaFX-Package-Sample-1.0.0.jar` 6 | ``` 7 | .\gradlew.bat clean 8 | .\gradlew.bat assemble 9 | ``` 10 | `maven` -> `.\target\JavaFX-Package-Sample-1.0.0.jar` 11 | ``` 12 | mvn clean 13 | mvn package 14 | ``` 15 | > `mvn package`执行时会将项目依赖的jar包拷贝到`.\target\alternateLocation\` 16 | 17 | ## 通过`java`命令来运行应用程序 18 | `gradle` 19 | 20 | 先执行`.\gradlew.bat copyDependencies`将项目依赖的jar包拷贝到`.\build\modules\` 21 | ```shell 22 | C:\CommandLineTools\Java\jdk-21\bin\java.exe -p ".\build\libs\;.\build\modules\" -m "sample/com.icuxika.MainApp" 23 | ``` 24 | `maven` 25 | ```shell 26 | C:\CommandLineTools\Java\jdk-21\bin\java.exe -p ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" -m "sample/com.icuxika.MainApp" 27 | ``` 28 | 29 | ## 通过`jdeps`命令可以显示项目依赖的模块 30 | `gradle` 31 | ```shell 32 | C:\CommandLineTools\Java\jdk-21\bin\jdeps.exe --print-module-deps --ignore-missing-deps --module-path .\build\modules\ .\build\libs\JavaFX-Package-Sample-1.0.0.jar 33 | ``` 34 | > 输出:MaterialFX,java.base 35 | 36 | `maven` 37 | ```shell 38 | C:\CommandLineTools\Java\jdk-21\bin\jdeps.exe --print-module-deps --ignore-missing-deps --module-path .\target\alternateLocation\ .\target\JavaFX-Package-Sample-1.0.0.jar 39 | ``` 40 | > 输出:MaterialFX,java.base 41 | 42 | ## 通过`jlink`构建出`.bat`形式的可执行程序 43 | `gradle` -> `.\build\jlink-build-dir\bin\JavaFX-Package-Sample.bat` 44 | ```shell 45 | C:\CommandLineTools\Java\jdk-21\bin\jlink.exe --module-path ".\build\libs\;.\build\modules\" --add-modules java.base,sample --launcher JavaFX-Package-Sample=sample/com.icuxika.MainApp --compress=zip-9 --no-header-files --no-man-pages --strip-debug --output .\build\jlink-build-dir 46 | ``` 47 | 48 | `maven` -> `.\target\jlink-build-dir\bin\JavaFX-Package-Sample.bat` 49 | ```shell 50 | C:\CommandLineTools\Java\jdk-21\bin\jlink.exe --module-path ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" --add-modules java.base,sample --launcher JavaFX-Package-Sample=sample/com.icuxika.MainApp --compress=zip-9 --no-header-files --no-man-pages --strip-debug --output .\target\jlink-build-dir 51 | ``` 52 | 53 | > 通过`jlink`构建出的文件夹`jlink-build-dir`已经可以压缩一下发到其他电脑上运行了,`.bat`脚本也有许多方式能够直接转为`.exe`,目前`jlink-build-dir`目录的大小为`89.7 MB` 54 | 55 | ## jpackage 使用一:以`jlink`命令构建出的目录`jlink-build-dir`为基础 56 | `gradle` -> `.\build\jpackage-build-dir\JavaFXSample\JavaFXSample.exe` 57 | ```shell 58 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample -m "sample/com.icuxika.MainApp" --runtime-image .\build\jlink-build-dir\ --icon .\src\main\resources\application.ico --app-version 1.0.0 --dest .\build\jpackage-build-dir\ 59 | ``` 60 | `maven` -> `.\target\jpackage-build-dir\JavaFXSample\JavaFXSample.exe` 61 | ```shell 62 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample -m "sample/com.icuxika.MainApp" --runtime-image .\target\jlink-build-dir\ --icon .\src\main\resources\application.ico --app-version 1.0.0 --dest .\target\jpackage-build-dir\ 63 | ``` 64 | 65 | > 目前`jpackage-build-dir`大小为`90.1 MB` 66 | 67 | ## jpackage 使用二:不以`jlink`构建结果为基础 68 | `gradle` -> `.\build\jpackage-direct-build-dir\JavaFXSample\JavaFXSample.exe` 69 | ```shell 70 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample --module-path ".\build\libs\;.\build\modules\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\build\jpackage-direct-build-dir 71 | ``` 72 | `maven` -> `.\target\jpackage-direct-build-dir\JavaFXSample\JavaFXSample.exe` 73 | ```shell 74 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample --module-path ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\target\jpackage-direct-build-dir 75 | ``` 76 | 77 | > 目前`jpackage-direct-build-dir`大小为`182 MB` 78 | 79 | ## 以下命令是构建Windows上`msi`格式的安装程序,需要配置[WiX Toolset v3.11.2](https://github.com/wixtoolset/wix3/releases/tag/wix3112rtm)的`bin`目录到环境变量`PATH`中 80 | 81 | ## jpackage 使用三:以`jpackage 使用二`的构建目录`jpackage-direct-build-dir`为基础 82 | `gradle` -> `.\build\jpackage-installer-dir\JavaFXSample-1.0.msi` 83 | ```shell 84 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --app-image .\build\jpackage-direct-build-dir\JavaFXSample\ --dest .\build\jpackage-installer-dir --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut 85 | ``` 86 | `maven` -> `.\target\jpackage-installer-dir\JavaFXSample-1.0.msi` 87 | ```shell 88 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --app-image .\target\jpackage-direct-build-dir\JavaFXSample\ --dest .\target\jpackage-installer-dir --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut 89 | ``` 90 | 91 | > 目前`JavaFXSample-1.0.msi`的大小为`65.9 MB` 92 | 93 | > 在jdk19、jdk20等版本有时候构建会遇到构建出的安装包是英文版本或者`exited with 311 code`的错误,请切换到`java20`分支此文件查看相应的解决方法,当前jdk21没有遇到这些问题 94 | 95 | ## jpackage 使用四:不以`jpackage 使用二`的构建结果为基础 96 | `gradle` -> `.\build\jpackage-direct-installer-dir\JavaFXSample-1.0.0.msi` 97 | ```shell 98 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --module-path ".\build\libs\;.\build\modules\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\build\jpackage-direct-installer-dir\ --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut 99 | ``` 100 | `maven` -> `.\target\jpackage-direct-installer-dir\JavaFXSample-1.0.0.msi` 101 | ```shell 102 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --module-path ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\target\jpackage-direct-installer-dir\ --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut 103 | ``` 104 | 105 | > 目前`JavaFXSample-1.0.msi`的大小为`65.9 MB` 106 | 107 | 108 | ## 最后 109 | `jlink`和`jpackage`两条命令最主要的使用方式和关键的参数都已经在上面了,不过本项目只演示了作为一个模块化项目时应用的方式,但是`jpackage`是支持为一个非模块化的可以通过`java -jar`运行的`jar`包生成对应的exe的。 110 | Java模块化尤其是这种纯命令行的方式难以处理很多情况比如非模块化的依赖,甚至同时支持模块化导入和兼容非模块化的依赖有时也会遇到问题,我已经有好长时间没写过`javafx`相关的代码,`jpackage`除了几个玩具项目几乎也没怎么用到过,以这点经验来说,还是找一个合适的插件为好,更简单的方式是在grdle中使用[The Badass Runtime Plugin](https://badass-runtime-plugin.beryx.org/releases/latest/)这个插件来对非模块化项目打包,这样的话项目中不用维护一个`module-info.java`文件,也不用考虑在引入新的依赖各种模块的冲突问题了 111 | 112 | 使用 mvn,配置一下阿里仓库的镜像就几乎不会有网络问题了,但是更好用的maven插件你可能需要多上上`Google`才能发现 113 | 114 | 使用 gradle,最大的问题就是你需要学会翻墙,并在IDEA中配置代理,命令行中 gradlew.bat 命令结尾添加`-D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890` 115 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder", 4 | "queryAllPublicMethods":true, 5 | "methods":[{"name":"","parameterTypes":[] }] 6 | }, 7 | { 8 | "name":"ch.qos.logback.classic.filter.ThresholdFilter", 9 | "queryAllPublicMethods":true, 10 | "methods":[{"name":"","parameterTypes":[] }, {"name":"setLevel","parameterTypes":["java.lang.String"] }] 11 | }, 12 | { 13 | "name":"ch.qos.logback.classic.joran.SerializedModelConfigurator", 14 | "methods":[{"name":"","parameterTypes":[] }] 15 | }, 16 | { 17 | "name":"ch.qos.logback.classic.pattern.DateConverter", 18 | "methods":[{"name":"","parameterTypes":[] }] 19 | }, 20 | { 21 | "name":"ch.qos.logback.classic.pattern.FileOfCallerConverter", 22 | "methods":[{"name":"","parameterTypes":[] }] 23 | }, 24 | { 25 | "name":"ch.qos.logback.classic.pattern.LevelConverter", 26 | "methods":[{"name":"","parameterTypes":[] }] 27 | }, 28 | { 29 | "name":"ch.qos.logback.classic.pattern.LineOfCallerConverter", 30 | "methods":[{"name":"","parameterTypes":[] }] 31 | }, 32 | { 33 | "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", 34 | "methods":[{"name":"","parameterTypes":[] }] 35 | }, 36 | { 37 | "name":"ch.qos.logback.classic.pattern.MessageConverter", 38 | "methods":[{"name":"","parameterTypes":[] }] 39 | }, 40 | { 41 | "name":"ch.qos.logback.classic.pattern.ThreadConverter", 42 | "methods":[{"name":"","parameterTypes":[] }] 43 | }, 44 | { 45 | "name":"ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter", 46 | "methods":[{"name":"","parameterTypes":[] }] 47 | }, 48 | { 49 | "name":"ch.qos.logback.classic.util.DefaultJoranConfigurator", 50 | "methods":[{"name":"","parameterTypes":[] }] 51 | }, 52 | { 53 | "name":"ch.qos.logback.core.ConsoleAppender", 54 | "queryAllPublicMethods":true, 55 | "methods":[{"name":"","parameterTypes":[] }] 56 | }, 57 | { 58 | "name":"ch.qos.logback.core.FileAppender", 59 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 60 | }, 61 | { 62 | "name":"ch.qos.logback.core.OutputStreamAppender", 63 | "methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }] 64 | }, 65 | { 66 | "name":"ch.qos.logback.core.UnsynchronizedAppenderBase", 67 | "methods":[{"name":"addFilter","parameterTypes":["ch.qos.logback.core.filter.Filter"] }] 68 | }, 69 | { 70 | "name":"ch.qos.logback.core.encoder.Encoder", 71 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 72 | }, 73 | { 74 | "name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder", 75 | "methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }] 76 | }, 77 | { 78 | "name":"ch.qos.logback.core.filter.Filter", 79 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 80 | }, 81 | { 82 | "name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase", 83 | "methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }] 84 | }, 85 | { 86 | "name":"ch.qos.logback.core.pattern.color.BoldGreenCompositeConverter", 87 | "methods":[{"name":"","parameterTypes":[] }] 88 | }, 89 | { 90 | "name":"ch.qos.logback.core.pattern.color.BoldYellowCompositeConverter", 91 | "methods":[{"name":"","parameterTypes":[] }] 92 | }, 93 | { 94 | "name":"ch.qos.logback.core.pattern.color.CyanCompositeConverter", 95 | "methods":[{"name":"","parameterTypes":[] }] 96 | }, 97 | { 98 | "name":"ch.qos.logback.core.rolling.RollingFileAppender", 99 | "queryAllPublicMethods":true, 100 | "methods":[{"name":"","parameterTypes":[] }, {"name":"setFile","parameterTypes":["java.lang.String"] }, {"name":"setRollingPolicy","parameterTypes":["ch.qos.logback.core.rolling.RollingPolicy"] }] 101 | }, 102 | { 103 | "name":"ch.qos.logback.core.rolling.RollingPolicy", 104 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 105 | }, 106 | { 107 | "name":"ch.qos.logback.core.rolling.RollingPolicyBase", 108 | "methods":[{"name":"setFileNamePattern","parameterTypes":["java.lang.String"] }, {"name":"setParent","parameterTypes":["ch.qos.logback.core.FileAppender"] }] 109 | }, 110 | { 111 | "name":"ch.qos.logback.core.rolling.TimeBasedRollingPolicy", 112 | "queryAllPublicMethods":true, 113 | "methods":[{"name":"","parameterTypes":[] }, {"name":"setMaxHistory","parameterTypes":["int"] }, {"name":"setTotalSizeCap","parameterTypes":["ch.qos.logback.core.util.FileSize"] }] 114 | }, 115 | { 116 | "name":"ch.qos.logback.core.rolling.helper.DateTokenConverter", 117 | "methods":[{"name":"","parameterTypes":[] }] 118 | }, 119 | { 120 | "name":"ch.qos.logback.core.spi.ContextAware", 121 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 122 | }, 123 | { 124 | "name":"ch.qos.logback.core.util.FileSize", 125 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 126 | }, 127 | { 128 | "name":"com.icuxika.MainApp", 129 | "methods":[{"name":"","parameterTypes":[] }, {"name":"main","parameterTypes":["java.lang.String[]"] }] 130 | }, 131 | { 132 | "name":"com.sun.glass.ui.Screen" 133 | }, 134 | { 135 | "name":"com.sun.javafx.font.directwrite.DWFactory", 136 | "methods":[{"name":"getFactory","parameterTypes":[] }] 137 | }, 138 | { 139 | "name":"com.sun.javafx.logging.PrintLogger", 140 | "methods":[{"name":"createInstance","parameterTypes":[] }] 141 | }, 142 | { 143 | "name":"com.sun.javafx.logging.jfr.JFRPulseLogger", 144 | "methods":[{"name":"createInstance","parameterTypes":[] }] 145 | }, 146 | { 147 | "name":"com.sun.javafx.scene.control.skin.Utils", 148 | "methods":[{"name":"getResource","parameterTypes":["java.lang.String"] }] 149 | }, 150 | { 151 | "name":"com.sun.javafx.tk.quantum.QuantumToolkit", 152 | "methods":[{"name":"","parameterTypes":[] }] 153 | }, 154 | { 155 | "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", 156 | "methods":[{"name":"","parameterTypes":[] }] 157 | }, 158 | { 159 | "name":"com.sun.prism.GraphicsPipeline", 160 | "methods":[{"name":"getFontFactory","parameterTypes":[] }, {"name":"getPipeline","parameterTypes":[] }] 161 | }, 162 | { 163 | "name":"com.sun.prism.shader.FillCircle_Color_Loader", 164 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 165 | }, 166 | { 167 | "name":"com.sun.prism.shader.FillPgram_Color_Loader", 168 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 169 | }, 170 | { 171 | "name":"com.sun.prism.shader.Solid_Color_Loader", 172 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 173 | }, 174 | { 175 | "name":"com.sun.prism.shader.Solid_TextureFirstPassLCD_Loader", 176 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 177 | }, 178 | { 179 | "name":"com.sun.prism.shader.Solid_TextureRGB_Loader", 180 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 181 | }, 182 | { 183 | "name":"com.sun.prism.shader.Solid_TextureSecondPassLCD_Loader", 184 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 185 | }, 186 | { 187 | "name":"com.sun.prism.shader.Texture_Color_Loader", 188 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 189 | }, 190 | { 191 | "name":"java.io.FilePermission" 192 | }, 193 | { 194 | "name":"java.lang.Character", 195 | "methods":[{"name":"isIdeographic","parameterTypes":["int"] }] 196 | }, 197 | { 198 | "name":"java.lang.Class", 199 | "methods":[{"name":"forName","parameterTypes":["java.lang.Module","java.lang.String"] }, {"name":"getModule","parameterTypes":[] }] 200 | }, 201 | { 202 | "name":"java.lang.Module", 203 | "methods":[{"name":"getDescriptor","parameterTypes":[] }, {"name":"getLayer","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }] 204 | }, 205 | { 206 | "name":"java.lang.ModuleLayer", 207 | "methods":[{"name":"boot","parameterTypes":[] }, {"name":"findModule","parameterTypes":["java.lang.String"] }] 208 | }, 209 | { 210 | "name":"java.lang.RuntimePermission" 211 | }, 212 | { 213 | "name":"java.net.NetPermission" 214 | }, 215 | { 216 | "name":"java.net.SocketPermission" 217 | }, 218 | { 219 | "name":"java.net.URLPermission", 220 | "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] 221 | }, 222 | { 223 | "name":"java.nio.ByteBuffer", 224 | "methods":[{"name":"order","parameterTypes":["java.nio.ByteOrder"] }] 225 | }, 226 | { 227 | "name":"java.nio.ByteOrder", 228 | "methods":[{"name":"nativeOrder","parameterTypes":[] }] 229 | }, 230 | { 231 | "name":"java.security.AllPermission" 232 | }, 233 | { 234 | "name":"java.security.SecurityPermission" 235 | }, 236 | { 237 | "name":"java.util.PropertyPermission" 238 | }, 239 | { 240 | "name":"java.util.concurrent.atomic.AtomicBoolean", 241 | "fields":[{"name":"value"}] 242 | }, 243 | { 244 | "name":"java.util.concurrent.atomic.AtomicReference", 245 | "fields":[{"name":"value"}] 246 | }, 247 | { 248 | "name":"javafx.animation.KeyValue" 249 | }, 250 | { 251 | "name":"javafx.scene.Camera" 252 | }, 253 | { 254 | "name":"javafx.scene.Node" 255 | }, 256 | { 257 | "name":"javafx.scene.ParallelCamera" 258 | }, 259 | { 260 | "name":"javafx.scene.Parent" 261 | }, 262 | { 263 | "name":"javafx.scene.Scene" 264 | }, 265 | { 266 | "name":"javafx.scene.control.Control" 267 | }, 268 | { 269 | "name":"javafx.scene.image.Image" 270 | }, 271 | { 272 | "name":"javafx.scene.layout.Pane" 273 | }, 274 | { 275 | "name":"javafx.scene.layout.Region" 276 | }, 277 | { 278 | "name":"javafx.scene.shape.Circle" 279 | }, 280 | { 281 | "name":"javafx.scene.shape.Rectangle" 282 | }, 283 | { 284 | "name":"javafx.scene.shape.Shape" 285 | }, 286 | { 287 | "name":"javafx.scene.text.Font" 288 | }, 289 | { 290 | "name":"javafx.scene.text.Text" 291 | }, 292 | { 293 | "name":"javafx.scene.transform.Transform" 294 | }, 295 | { 296 | "name":"javafx.stage.Stage" 297 | }, 298 | { 299 | "name":"javafx.stage.Window" 300 | }, 301 | { 302 | "name":"javax.smartcardio.CardPermission" 303 | }, 304 | { 305 | "name":"sun.misc.Unsafe", 306 | "fields":[{"name":"theUnsafe"}] 307 | }, 308 | { 309 | "name":"sun.security.provider.MD5", 310 | "methods":[{"name":"","parameterTypes":[] }] 311 | } 312 | ] 313 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.icuxika 8 | JavaFX-Package-Sample 9 | 1.0.0 10 | 11 | 12 | UTF-8 13 | 21 14 | 21 15 | 16 | 17 | 18 | 19 | org.openjfx 20 | javafx-base 21 | ${javafx.version} 22 | 23 | 24 | org.openjfx 25 | javafx-controls 26 | ${javafx.version} 27 | 28 | 29 | org.openjfx 30 | javafx-graphics 31 | ${javafx.version} 32 | 33 | 34 | org.openjfx 35 | javafx-fxml 36 | ${javafx.version} 37 | 38 | 39 | org.openjfx 40 | javafx-swing 41 | ${javafx.version} 42 | 43 | 44 | org.openjfx 45 | javafx-media 46 | ${javafx.version} 47 | 48 | 49 | org.openjfx 50 | javafx-web 51 | ${javafx.version} 52 | 53 | 54 | io.github.palexdev 55 | materialfx 56 | 11.16.1 57 | 58 | 59 | org.slf4j 60 | slf4j-api 61 | 2.0.12 62 | 63 | 64 | ch.qos.logback 65 | logback-classic 66 | 1.5.3 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 3.10.1 75 | 76 | ${maven.compiler.release} 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-dependency-plugin 82 | 3.1.2 83 | 84 | 85 | copy-dependencies 86 | package 87 | 88 | copy-dependencies 89 | 90 | 91 | ${project.build.directory}/alternateLocation 92 | false 93 | false 94 | true 95 | 96 | 97 | 98 | 99 | 100 | org.codehaus.mojo 101 | exec-maven-plugin 102 | 3.0.0 103 | 104 | 105 | image 106 | 107 | exec 108 | 109 | 110 | jpackage 111 | ${project.basedir} 112 | 113 | -n 114 | JavaFXSample 115 | -t 116 | app-image 117 | -p 118 | 119 | ${project.build.directory}/alternateLocation${path.separator}${project.build.directory}/${project.artifactId}-${project.version}.jar 120 | 121 | -d 122 | ${project.build.directory}/mvnPackage 123 | -m 124 | sample/com.icuxika.MainApp 125 | --icon 126 | 127 | ${project.basedir}/src/main/resources/application.ico 128 | 129 | --app-version 130 | 1.0.0 131 | 132 | 133 | 134 | 135 | installer 136 | 137 | exec 138 | 139 | 140 | jpackage 141 | ${project.basedir} 142 | 143 | -n 144 | JavaFXSample 145 | -t 146 | dmg 147 | -p 148 | 149 | ${project.build.directory}/alternateLocation${path.separator}${project.build.directory}/${project.artifactId}-${project.version}.jar 150 | 151 | -d 152 | ${project.build.directory}/mvnInstaller 153 | -m 154 | sample/com.icuxika.MainApp 155 | --icon 156 | 157 | ${project.basedir}/src/main/resources/application.ico 158 | 159 | --app-version 160 | 1.0.0 161 | 162 | 163 | 164 | 165 | 166 | 167 | org.openjfx 168 | javafx-maven-plugin 169 | 0.0.8 170 | 171 | true 172 | 2 173 | true 174 | true 175 | JavaFXSample 176 | build-link 177 | sample-zip 178 | sample/com.icuxika.MainApp 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | win 187 | 188 | 189 | 190 | com.gluonhq 191 | gluonfx-maven-plugin 192 | 1.0.22 193 | 194 | com.icuxika.MainApp 195 | 196 | LanguageResource 197 | LanguageResource_en 198 | LanguageResource_zh_CN 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | mac 207 | 208 | 209 | 210 | com.gluonhq 211 | gluonfx-maven-plugin 212 | 1.0.22 213 | 214 | com.icuxika.MainApp 215 | 216 | LanguageResource 217 | LanguageResource_en 218 | LanguageResource_zh_CN 219 | 220 | 221 | 222 | -Dsvm.platform=org.graalvm.nativeimage.Platform$MACOS_AMD64 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | --------------------------------------------------------------------------------