Java Developers Summit Online 2023 に参加しました!
2023年2月28日にオンライン開催された Java Developers Summit
に初めて参加しました。
恥ずかしながら GraalVM
周りの仕組みや JIT
コンパイルとの違いなど、初めて学ぶことが多かったです。
今回は、GraalVM
と SpringBoot3.0
で導入されたネイティブイメージのサポート機能を簡単なサンプルアプリケーションを作って試してみたいと思います。
GraalVM のインストール
私のローカルPCは MacBook のため、brew
コマンドでインストールしました。
インストール後に java --version
を実行すると下記のようになりました。
openjdk 17.0.6 2023-01-17
OpenJDK Runtime Environment GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13, mixed mode, sharing)
GraalVM
がインストールされたようですね!
SpringBoot3.0 のサンプルアプリケーションの準備
今回は下記を満たす簡易機能を実装しようと思います。
/users
でユーザー情報全件を取得する- ユーザー情報は
h2
の組み込みデータベース上に登録しておく - DB アクセスは
JdbcTemplate
で実施する(MyBatis などがネイティブ対応しているか不明だったため)
Spring Initializr で Gradle プロジェクトを作成
Spring Initializr で必要な情報を選択し、Gradle プロジェクトを作成します。
- Gradle - Groovy
- Java 17
- Spring Boot 3.0.3
- Artifact -
native-image-rest-api
- Dependencies
- Spring Web
- H2 Database
- Spring Data JDBC
- GraalVM Native Support 今回の主役です!
IntelliJ で実装開始
ダウンロードした zip ファイルを開き、builde.gralde
を(大好きな) IntelliJ で開きます。
そして早速エラーが出ました。。。
No matching variant of org.springframework.boot:spring-boot-gradle-plugin:3.0.3 was found.
The consumer was configured to find a runtime of a library compatible with Java 11,
packaged as a jar, and its dependencies declared externally,
as well as attribute 'org.gradle.plugin.api-version' with value '7.6.1' but:
Java のバージョンが 17 になっていないようなので、IntelliJ の Preferences から Gradle の Java バージョンを 17 に変更して再トライしました。
BUILD SUCCESSFUL in 10s
無事に通過しました!
IntelliJ で実装開始(再トライ)
users
テーブルから user
のリストを取得するということで、下記のようなクラスを一通り作成します。
- User レコード(Java の record です)
- UserController
- UserQueryService
- UserRepository(Interface)
- UserDataSource(implements UserRepository)
また、h2
組み込みデータベースを使用するので resources
配下に sql
ファイルを用意します。
- schema.sql(
users
テーブルの create) - data.sql(数十件のユーザーデータ)
文末に GitRepository
を載せておきますが、要素だけかいつまんで紹介します。
- User
public record User(String name, int age) {
}
- UserDataSource
@Repository
public class UserDataSource implements UserRepository {
JdbcTemplate jdbcTemplate;
public UserDataSource(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public List<User> findAll() {
return jdbcTemplate.query("select * from users"
, ((rs, rowNum) ->
new User(rs.getString("name"), rs.getInt("age")))
);
}
}
それ以外は基本的に 3 層アーキテクチャにのっとっています。
JVM 上で起動
まずはいつも通り実行してみます。
2023-03-01T17:31:24.125+09:00 INFO 51217 --- [ main] c.e.demo.NativeImageRestApiApplication : Started NativeImageRestApiApplication in 2.594 seconds (process running for 3.031)
起動しました!
(後で 2.594 seconds
とネイティブイメージでの起動時間を比較します)
実行結果も問題なさそうです。
% curl http://localhost:8080/users
[{"name":"れなーず太郎01","age":34},{"name":"れなーず太郎02","age":34},{"name":"れなーず太郎03","age":34},{"name":"れなーず太郎04","age":34},{"name":"れなーず太郎05","age":4},{"name":"れなーず太郎07","age":34},{"name":"れなーず太郎08","age":34},{"name":"れなーず太郎09","age":34},{"name":"れなーず太郎10","age":34}]
ネイティブイメージの作成
ここから本題です。
ネイティブイメージをビルドしてみようと思います。
gradle
タスクに nativeCompile
なるものがあるので実行してみます。
はい、失敗しました。。。
Execution failed for task ':nativeCompile'.
> Cannot query the value of property 'javaLauncher' because it has no value available.
javaLauncher
ってなんぞや???
と思いながらGraalVM のサイトなどをググりまして、build.gradle
に下記を記載してみました。
- build.gradle
graalvmNative {
binaries {
main {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(17)
vendor = JvmVendorSpec.matching("GraalVM Community")
}
mainClass = 'com.example.demo.NativeImageRestApiApplication'
}
}
}
そして再度 nativeCompile
タスクを実行するとコンパイルが始まりました!(よかった)
コンパイル自体は 4m17s
要しました。やはりネイティブイメージを作成するのと、普通に jar
パッキングするのとは訳が違いますね。
ネイティブイメージの起動
build/native/nativeCompile
に作成されるので、そこに cd
して起動してみます。
./native-image-rest-api
起動しました!
2023-03-01T17:45:16.155+09:00 INFO 51366 --- [ main] c.e.demo.NativeImageRestApiApplication : Started NativeImageRestApiApplication in 0.129 seconds (process running for 0.156)
そして起動が速いですね!0.129 seconds
です。
JVM 上だと 2.594 seconds
だったので、起動時間は元の 5% 程度まで短縮しました。(すごい!)
さらに実行結果ですが、
curl http://localhost:8080/users
[{"name":"れなーず太郎01","age":34},{"name":"れなーず太郎02","age":34},{"name":"れなーず太郎03","age":34},{"name":"れなーず太郎04","age":34},{"name":"れなーず太郎05","age":4},{"name":"れなーず太郎07","age":34},{"name":"れなーず太郎08","age":34},{"name":"れなーず太郎09","age":34},{"name":"れなーず太郎10","age":34}]%
JVM で実行した場合と変わりなく、結果を取得することができました。
まとめ
なんとなく聞いたことのあった GraalVM
について、サミットに参加したことで触れてみるきっかけとなりました。
やはりこういったイベントに参加することは有意義になりますね。
また、SpringBoot
が GraalVM
をサポートしたことにより、その可能性は色々と広がっていくかなと思います。
私は Java
大好きマンなので、マイクロサービスや FaaS
といったものが主流になりつつある今、JVM
の弱点がこのように改善されていくのは非常に嬉しく感じました。
最後に今回のサンプルコードはこちらにありますので、よかったらご覧ください。
https://github.com/leonards-architect/native-image-rest-api