torutkのブログ

ソフトウェア・エンジニアのブログ

JavaFXアプリケーションのJDK 11対応(NetBeans編)(モジュール対応編)

はじめに

じゃばえふえっくす Advent Calendar 2018 - Qiita20日目のエントリです。

前回のブログ(JavaFXアプリケーションのJDK 11対応(NetBeans編) - torutkのブログ)では、アプリケーションをJava SE 9から導入されたモジュールシステム(Java Platform Module System、以下JPMSと呼ぶ)には対応せずに、JavaFXライブラリをクラスパスで参照する方法でJDK 11に対応させました。

しかしながら、JPMS対応をしなかったため、実行時に--add-modulesを指定するか、メインクラス(javaランチャーから最初に指定するクラス)のロード時にJavaFXのクラスを参照しないようにする(javafx.application.Applicationの継承をやめる)必要があります。

そこで、今回のエントリではJPMS対応によるJDK 11対応をすることにします。

JPMS対応することで、上述の制約がなくなります。さらに、Java実行環境と一緒にアプリケーションを配布する際に、Java実行環境をアプリケーションに必要なモジュールに限定することで配布サイズ削減が図れます。jlinkコマンドでアプリケーションとアプリケーション実行に必要とするモジュールだけを1か所にまとめた実行イメージを作成できます*1

対応方法

プロジェクトの作成

NetBeans 10で[File]メニュー > [New Project...] で、カテゴリ[Java]から[Java Modular Project]を選択します。

f:id:torutk:20181209000836p:plain
NetBeans 10 New Project Java Modular Project

プロジェクト名とプロジェクトを作成するフォルダを指定します。

f:id:torutk:20181209001657p:plain
NetBeans 10 New Project Java Modular Project で プロジェクト名と場所を指定

Java Modular Projectで生成したプロジェクトでは、プロジェクトの下に複数のモジュールが定義できるマルチモジュールな構成となります。よって、プロジェクト作成後に、少なくとも1つのモジュールを定義します。

プロジェクトペインで今作成したプロジェクトを右クリックし、[New] > [Module]を選択します。

f:id:torutk:20181209002117p:plain
NetBeans 10 Java Modular Project に新しいモジュールを指定

モジュール名を指定します。モジュール名は、一般的にはモジュールに含むパッケージ名のうち代表的なものとします。

f:id:torutk:20181209002535p:plain
NetBeans 10 Project に新しいモジュールを指定

モジュールを作成すると、プロジェクトの配下にモジュールが追加され、その中にmodule-info.javaが生成されます。

f:id:torutk:20181209002845p:plain
NetBeans 10 Project に追加された新しいモジュール

JavaFXモジュールのパスを指定

プロジェクトのプロパティを開き、左側ペインで[Libraries]を選択、右側ペインの[Compile]タブで、Modulepath の右端の[+]をクリックします。ポップアップメニューが出るので、[Add Library」を選択、ライブラリ一覧からJavaFX 11を選択します(NetBeansのライブラリにJavaFX 11を追加する方法は、JavaFXのNetBeans設定 - ソフトウェアエンジニアリング - Torutk参照)。

f:id:torutk:20181209004431p:plain
NetBeans 10 Project のモジュールパスにJavaFX 11を指定

ソースファイルの移動

NetBeans 8.2/JDK 8で作成したJavaFXアプリケーションのソースファイルを、新しく作成した Java Modular Projectのプロジェクトへ移動します。NetBeans 10で旧プロジェクトを開き、ソースファイルを含むパッケージをマウスでドラッグし、新プロジェクトのモジュールの下の[classes]にドロップします。

f:id:torutk:20181219072428p:plain
旧プロジェクトから新プロジェクトへのソースファイル移動

そのままドロップするとフォルダが移動になるので、コピーするにはCtrlキーを押しながらドロップします。

module-info.javaの定義

プロジェクトのライブラリ定義のモジュールパスにJavaFX 11ライブラリを指定しただけでは、アプリケーションからJavaFXのクラスを参照することはできません。JavaFXライブラリのクラスを参照するimport文がエラーになってしまいます。

f:id:torutk:20181209005220p:plain
NetBeans 10 Project のモジュールパスにJavaFX 11を指定しただけでは参照を解決できず

そこで、プロジェクトのモジュールパスにJavaFXライブラリを指定した後、module-info.javaに使いたいクラスが含まれるモジュールを定義します。

module com.torutk.gadget.image {
    requires javafx.controls;
    requires javafx.fxml;
    opens com.torutk.gadget.image to javafx.graphics, javafx.fxml;
}
  • 基本はjavafx.controls モジュールへの依存を記述。javafx.controlsからjavafx.graphicsモジュールへの依存の推移により暗黙的な依存が定義されるので、javafx.graphicsへの依存は記述不要。
  • FXMLを使う場合は、javafx.fxmlモジュールへの依存を記述。
  • アプリケーションのメインクラス(javafx.application.Applicationクラスを継承するアプリケーションクラス)を持つパッケージ名をopensで記述。

ビルド成果物

プロジェクトの下に定義したモジュール毎に、ビルド成果物であるモジュールファイル(JARファイル)が生成されます。

f:id:torutk:20181220061707p:plain
ビルド成果物

JARファイルの内容が次です。モジュール記述子(module-info.class)が含まれています。

f:id:torutk:20181220071325p:plain
モジュールファイル(JAR)の内容

コマンドラインからの実行

NetBeans上でビルドしたJARファイルをコマンドラインから実行するには、少々長いオプションを指定します。

D:\work\ImageGadget> java --module-path "C:\Program Files\Java\JavaFX\javafx-sdk-11.0.1\lib";dist -m com.torutk.gadget.image/com.torutk.gadget.image.ImageGadgetApp
  • JavaFX 11ライブラリは、C:\Program Files\Java\JavaFX\javafx-sdk-11.0.1 にインストール
  • アプリケーションのモジュールJARファイルは、カレントディレクトリのdist下にある

NetBeans上でメインクラスの指定をJARマニフェストにする方法が見つからなかったのでオプションが長くなってしまいました。

まとめ

  • アプリケーションをJPMS対応するには、NetBeansのプロジェクト種類でJava Modular Projectを選ぶ
  • Java Modular Projectで生成したプロジェクトでは、最低1つはモジュールを作成する
  • JDK標準以外のライブラリを利用するときは、プロジェクトのプロパティでライブラリをモジュールパスに登録する(クラスパスではなく)
  • java.baseモジュールに含まれるクラス以外を利用するときは、モジュール定義(module-info.java)に依存を記述する

*1:@skrbさんのコメントをもとに文章を修正しました。モジュール対応・非モジュール対応のアプリケーションの実行イメージの作成については、 JavaFXアプリケーションのJDK 11対応(配布編) - torutkのブログ に書きました。