JavaOne2017を前にして、待望のJava9が遂にリリースしましたね!
さて、Java9といえばやはり気になるのはjigsawによるモジュール機能です。モジュールの使い方までは良く見ますが、jlinkが個人的には気になってたので試した結果をまとめました。
はじめに
Jigsawに関してですが少なくとも現時点では、fat-jarやgo言語のようなシングルバイナリを代替するようなことは単独ではできません。
ただ、モジュールとjlinkを使うことでアプリケーションを含んだ配布用のJVMを生成することが可能で、今回はそれについての説明になります。
モジュールで公開範囲の改善や依存の早期発見ができるようになったことは特に触れないので、その辺はこの記事とかを参考にされると良いと思います。
コードの準備
まずは、コードの準備です。
下記のような感じでアプリからライブラリが三階層で呼ばれてるようなサンプルを作ります。
こちらにできたものが。特に語ることがないので詳細は省きますが、それぞれのトップディレクトリにmodule-info.java
を配置して、モジュールの設定を書いています。
https://github.com/koduki/example-jlink
ビルド
続いて各モジュールのビルドです。
その前にまずモジュール配置先のディレクトリを作ります。
$ mkdir modules
当たり前ですが、依存が無い方から順番にビルドしていきます。
$ javac -d commons/classes/ -cp commons/src/main/java $(find commons/src/main/java -name "*.java")
$ jar cvf modules/commons.jar -C commons/classes/ .
ビルドは基本的には通常のjarと同じで良さそうですが、コンパイル時にp
オプションでモジュールのディレクトリを指定するところが違います。
また、古いバージョンだとmp
としてオプションが記載されているので要注意です。
$ javac -d libs/classes/ -cp "libs/src/main/java/" -p modules/ $(find libs/src/main/java -name "*.java")
$ jar cvf modules/libs.jar -C libs/classes/ .
ライブラリがビルドできたらメインアプリケーション。とはいえこれも他と同様です。
$ javac -d apps/classes/ -cp apps/src/ -p modules $(find apps/src -name "*.java")
$ jar cvf modules/app.jar -C apps/classes/ .
以下のような形で実行できます。
$ java -p modules/ -m apps/cn.orz.pascal.app.MyApp
Hello, cn.orz.pascal.common.CommonLib
Hello, cn.orz.pascal.lib.MyLib
Hello, MyApp
実行が確認できましたね? モジュールを指定するときはm
オプションでモジュール名/実行するクラス
という形で記述します。
JLinkによる配布用JVMの作成
これだけだと、配布観点ではあまり嬉しく無いので、いよいよJLinkの出番です。
JLinkを使えば指定したモジュールに必要な最小構成のJVMが作れます。つまり、WebアプリでSwingやAWTのライブラリを持たなくて良くなるという事です。
Macの場合はインストールしただけだとパスが通って無いので下記のように対応します。
$ export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/
$ alias jlink=${JAVA_HOME}/bin/jlink
続いてjlinkによるリンクの実行です。今回は合わせてapps/cn.orz.pascal.app.MyApp
を実行するランチャーの作成と、全体のzipでの圧縮をしています。
$ jlink --compress=2 --module-path $JAVA_HOME/jmods:modules --add-modules apps --output dist/my-app --launcher myapp=apps/cn.orz.pascal.app.MyApp
実行するとdistディレクトリにmy-appができています。実行すると以下の通り。
$ ./dist/my-app/bin/myapp
Hello, cn.orz.pascal.common.CommonLib
Hello, cn.orz.pascal.lib.MyLib
Hello, MyApp
my-appには指定したモジュールと最小構成のJVMが入ってるのでこのディレクトリを配布するだけで、JVMが無い環境でもそのまま動きます。ポータブルですね!
なお、容量は今回だと下記の通り。わりといい感じです。
$ du -h dist|tail -n1
24M dist
まとめ
jlinkによるアプリ配布向けのJVMの作り方について試してみました。パッケージングこそされないものの、JVMがインストールされてない環境への配布もずいぶん楽そうです。
サーバ用途ならDockerにパッケージさせてしまえば特に困らないんじゃ無いでしょうか?
コマンド系など、go言語やjavaのfat-jarで実現してたようなシングルバイナリでの配布には向きませんが、今後electron-packagerみたいなのが作られれば、十分実現できると思います。
それでは、Happy Hacking!