2019新春支付寶紅包技術大揭祕在線峯會將於03-07日開始,點擊這裏報名屆時便可參與大牛互動。css
Java 11 自 2018.9.25 發佈以來,已經好幾個月了,在還沒正式 GA 以前都習慣性的去 java-countdown.xyz check 發佈倒計時。Java 11 有比較多的新功能,而其中最吸引個人html
Java 11 是 LTS 版本前端
在 11 發佈時,JavaFX 也發佈了 11 的 GA 版本。JavaFX 自己並不新奇,但自 Java9 模塊化後,JavaFX 得益於 jlink 的能力,可以將 JavaFX 封裝爲獨立的 GUI 應用,不要求安裝JDK 。這使得在桌面應用開發的場景,除了 Electron、Mono、QT 等跨平臺開發框架,Java 也能做爲其中的一項選擇了。在 Swing 時代,Java的桌面應用開發體驗也不差(曾經作過的小遊戲 wenerme/GTetris),但因爲累贅的 JDK (大約 150m)使得開發一個小應用變得不切實際。java
JLink 能夠將項目依賴的模塊加上基礎VM來生成一個新的 JDK,應用的體積可以大大減少,若是還能再配合 progard,那體積還能再縮小一圈。git
基於體驗 Java11 和 JavaFX 的前提(每一個Java程序員都會寫界面是常識?),將生成 奧格人羣服務化接口文檔 的生成器作成了一個 GUI 工具,源碼在 wener.cyw/tools。程序員
工具下載地址見附件 - 只打包了 Mac 版應用,由於沒有 Windows。github
從 Java 11 開始,Oracle 的 JDK 便再也不建議使用了,所以首選 OpenJDK,而 OpenJDK 的二進制提供方也有很多,在這裏推薦使用 adoptopenjdk,與 Oracle 不一樣的是,在這裏下載的 JDK 都是壓縮包,無須安裝,解壓就能使用,固然也不會有自動更新的能力。apache
點擊前往下載api
下載後我解壓到了 ~/jdk
目錄,而後創建軟鏈接 ~/jdk/11
指向到了該版本。bash
總結一下在整個過程當中遇到的問題
搭建一個 Java 11 的 Maven 項目與搭建一個普通的項目區別並不大,只是會多一些配置,而且全部的依賴都須要使用最新的。
父 POM 的 build/plugins 配置說明
<!-- 對 Java 11 持有基本的尊敬 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> <source>11</source> <target>11</target> </configuration> </plugin> <!-- 打包時打包到 modules 目錄 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <outputDirectory> ${project.build.directory}/modules </outputDirectory> </configuration> </plugin> <!-- 將依賴拷貝到 modules 目錄 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory> ${project.build.directory}/modules </outputDirectory> <includeScope>runtime</includeScope> </configuration> </execution> </executions> </plugin> <!-- 由於並非全部依賴都是模塊化的,因此可能會出現 illegal-access 的問題 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.0</version> <configuration> <argLine> --illegal-access=permit </argLine> <forkCount>0</forkCount> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.22.0</version> <configuration> <argLine> --illegal-access=permit </argLine> </configuration> </plugin>
應用項目的 build 配置
<build> <!-- 由於用到了 fxml,且 fxml 是放在類旁邊的,因此須要手動指定該類資源 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.fxml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <!-- 確保 jar 中生成正確的信息 --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <executions> <execution> <id>module-main-class</id> <phase>package</phase> <goals> <goal>exec</goal> </goals> <configuration> <!-- 由於 PATH 中的 jar 是 Java8,因此這裏指定的絕對路徑 --> <executable>/Users/wener/jdk/11/Contents/Home/bin/jar</executable> <arguments> <argument> --update </argument> <argument> --file=${project.build.directory}/modules/${project.build.finalName}.jar </argument> <!-- 啓動類 --> <argument> --main-class=me.wener.tools.app.AppMain </argument> <argument> --module-version=${project.version} </argument> </arguments> </configuration> </execution> </executions> </plugin> </plugins> </build>
最終的配置在 mvn package
後,會在 target/modules
目錄下看到全部的 jar 包。這裏的 jar 在生成 JDK 時會用到。
在項目搭建好後,創建出對應的子模塊,且在子模塊中 src/main/java
設置好 module-info.java
JavaFX 的開發很是有意思,由於可使用 FXML,開發的過程體驗與 React/Vue/Angular 這樣的前端開發體驗很是類似,只須要在 FXML 作好佈局,在 css 中定義好樣式,而後綁定好交互處理方法便可。
應用的啓動類
public class AppMain extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("scene/Main.fxml")); Scene scene = new Scene(root, 640, 480); stage.setTitle("@文邇 的小工具"); stage.setScene(scene); stage.show(); } }
由於是基於 fxml,啓動類只須要將該場景初始化展現便可。
一個 fxml 的基本框架
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.stage.Screen?> <AnchorPane fx:id="masterPane" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.wener.tools.app.scene.MainScene"> </AnchorPane>
其中比較關鍵的是 fx:controller
綁定了控制類 me.wener.tools.app.scene.MainScene
。
所以在後續的 action
定義中可直接引用控制類上的方法,或者將頁面元素直接關聯到控制類。
綁定元素
元素關聯
Intellij 比較智能,可直接在這兩個地方互相跳轉。
<!-- 按鈕點擊關聯控制類上的方法 doConvert --> <Button mnemonicParsing="false" onAction="#doConvert" text="生成文檔"/>
在 APP 開發完成後,便可爲該 APP 生成一個定製的 JDK,該 JDK 只須要包含 APP 所需依賴,生成的 JDK 可重複使用,除非 APP 的依賴變動。
# 確保下面的 Java 命令是 Java 11 的 export PATH=$JAVA_11_HOME/bin:$PATH # 查看打包拷貝的模塊 # 其中會發現不少 automatic 的模塊 java --list-modules -p target/modules/ # 查看主應用 jar 的依賴請求 jdeps --module-path target/modules/ target/modules/tools-app-1.0-SNAPSHOT.jar # 生成 JDK 到該目錄 jdk/Contents/Home/jre # add-modules 的列表來自於 module-info 的定義 jlink --strip-debug --compress 2 \ --no-header-files --no-man-pages \ --output jdk/Contents/Home/jre \ -p $PWD/target/modules \ --add-modules javafx.controls,javafx.fxml,com.google.common,com.github.javaparser.core,com.github.javaparser.symbolsolver.logic,com.github.javaparser.symbolsolver.model,me.wener.tools.core
但在生成 JDK 時會發現異常
Error: automatic module cannot be used with jlink: com.github.javaparser.symbolsolver.logic from xxx.jar
異常的緣由是 jlink 不支持 automatic 的模塊,所謂 automatic 模塊,指的是沒有 module-info 的模塊,但在 jar 的 META-INF/MANIFEST.MF
中定義了 Automatic-Module-Name
信息。
針對這類 jar,惟一能比較好的處理方式
一下以 javax.inject 爲案例
wget http://central.maven.org/maven2/javax/inject/javax.inject/1/javax.inject-1.jar # 查看依賴狀況,非模塊化的 jar 依賴和模塊化 jar 的依賴現實不一樣 # 輸出: javax.inject-1.jar -> java.base # 模塊化的 jar 輸出: javax.inject -> java.base jdeps javax.inject-1.jar # 生成 module-info.java jdeps --generate-open-module info javax.inject-1.jar # 解壓 jar unzip javax.inject-1.jar -d classes/ # 編譯 module-info.java javac -p javax.inject -d classes/ info/javax.inject/module-info.java # 更新 jar jar uf javax.inject-1.jar -C classes/ module-info.class # 再次查看依賴 jdeps javax.inject-1.jar
其中 info/javax.inject/module-info.java
的內容爲
open module javax.inject { }
接下來的一段時間即是將全部用到的依賴進行這樣的轉換,其中須要注意的是 間接依賴也須要模塊處理。其中最難處理的是 guava,由於須要將 guava 模塊化,也須要它依賴的全部模塊都存在。
open module com.google.common { requires j2objc.annotations; requires java.logging; requires jdk.unsupported; requires jsr305; requires transitive error.prone.annotations; }
所以爲了將 guava 模塊化,須要從 maven 上下載全部的這些 jar 並進行模塊化。
完成全部的模塊化後,再次經過 jlink 生成 jdk 到 jdk/Contents/Home/jre
,之因此生成到這樣的一個目錄,是由於在應用打包時能符合默認的 Java 目錄結構。
# 使用生成的 JDK 來運行應用 ./jdk/Contents/Home/jre/bin/java -Xmx64m --upgrade-module-path target/modules -m me.wener.tools.app # 生成的 JDK 大約 50m - 對此已經很是滿意了,Electron 通常都是 100m 左右 du -s jdk/
應用打包主要是將如今已經能運行的 jdk 環境打包爲一個 macOs 的 app。打包器有不一樣的選擇,但用下來仍是 jar2app 比較好用。若是須要打包其它平臺應用,須要選擇其它平臺的打包器。
git clone https://github.com/Jorl17/jar2app # jar2app 是 Python 腳本,所以須要 Python 環境 # 打包,使用自定義 jdk target/jdk ./jar2app/jar2app ./target/modules/tools-app-1.0-SNAPSHOT.jar -r target/jdk/ -b me.wener.tools -n WenerTools -j "-Xmx64M --upgrade-module-path $APP_ROOT/Contents/PlugIns/jdk/modules" # 最終打包後的應用約 50m # 50M WenerTools.app du -s WenerTools.app # 雙擊啓動或命令行啓動 open WenerTools.app
一切大功告成,一個 APP 就此誕生了!若是還想要提交到 AppStore,這個過程還會須要其餘的很多步驟,在這就不詳細說明啦。
應用開發過程,打包過程都仍是比較愉快的,最困難的是模塊化 jar 的處理,由於不少模塊都尚未 module-info.java,致使大部分的 jar 都得先處理一遍,不過這個過程是能夠累計的,被處理過的 jar 能夠被重複利用。若是不須要配合 jlink,那麼是不須要處理的。
Java 11 意味着 Java 九、十、11 的全部新特性,JavaFX 開發也異常的簡單,整個過程仍是很爽的!
點擊閱讀更多,查看更多詳情