springboot2系からspringboot3系にアップデートするため、javaをアップデートすることになった その時に行ったことと困ったことをまとめた
行ったこと
ローカルの設定
IDEの設定
IDEの設定をJava17仕様にする IDEは色々あると思うので割愛
JAVA_HOMEの変更
ローカルのjavaをjdk17にしておく必要がある。 以下のように表示されてればOK
java -version
openjdk version "17.0.11" 2024-04-16 LTS
OpenJDK Runtime Environment Corretto-17.0.11.9.1 (build 17.0.11+9-LTS)
OpenJDK 64-Bit Server VM Corretto-17.0.11.9.1 (build 17.0.11+9-LTS, mixed mode, sharing)
gradlewのバージョンアップ
自分のプロジェクトではビルドにgradlewを使っている
gradleドキュメントによると、java17ではgradle7.3までアップデートしなければならない
gradle/wrapper/gradle-wrappper.propertiesのdistributionUrlを以下のように変更した
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
そして、以下のコマンドでgradlewをアップデートした
./gradlew wrapper --gradle-version=7.3
kotlinDSLの修正
自分が触っているプロジェクトでは、kotlin dslでビルドスクリプトが記載されている
java17を利用するようにビルドスクリプトを修正し、以下のコマンドで出たERRORやWARNINGを修正した
./gradlew clean
./gradlew build
関連するライブラリのバージョン修正
ライブラリのバージョンスキューなどを確認し、java17に対応しているか確認した
特に記載のないものは一旦保留した
Dockerfileのベースイメージ修正
自分のプロジェクトではアプリケーションをコンテナ化している。 そのためDockerfileが存在するが、ベースイメージを変更する必要があった
FROM amazoncorretto:17-alpine
動作確認・デプロイ
javaのリリースノートを確認して変更点や影響がなさそうか確認するが、結局のところほとんどわからない
動作確認が一番重要
単体テスト・シナリオテストを行い、数日はステージング環境で様子を見た
ステージングにて問題がないことを確認し、本番でカナリアデプロイを行い、無事事故なくリリースすることができた
困ったこと
DateTime.now()がミリ秒からナノ秒を持つようになた
DateTime.now()が、Java8の時はミリ秒までしか持たなかったがJava17になるとナノ秒を持つようになった それが原因で単体テストに引っかかったため、DateTime.now()は全てtruncate()するように修正した
java17に対応していないライブラリ問題
java17に対応していないライブラリで以下の2つエラーが確認された
sun.*系のライブラリの廃止
あるライブラリで以下のエラーが発生
Caused by: java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder
こちらによると、java9以降ではsun.*系のパッケージが廃止されたので、これをインポートするとエラーが発生するとのこと
sun.mics.BASE64Encoderの代わりにjava.util.Base64を使えば良さそう
ライブラリ側に対応して貰えない限りは、ライブラリを強制的に書き換えたり、同じ名前で上書きして実装したりする必要がある
今回は、ライブラリ側が対応してくれた
java17以降から内部APIにアクセスできなくなった
以下のエラーが発生
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field protected java.lang.String java.net.HttpURLConnection.method accessible: module java.base does not "opens java.net" to unnamed module @3625a016
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
java17以降から内部API(今回だとjava.net)にアクセスできなくなったらしい https://qiita.com/nowokay/items/ec58bf8f30d236a12acb#jep-403-strongly-encapsulate-jdk-internals https://www.infoq.com/jp/news/2021/07/internals-encapsulated-jdk17/
こちらはどうしても使わなければならないライブラリだったので、jvm起動の際に以下のオプションを追加することで解決した(非推奨ではあるが。。)
--add-opens=java.base/java.net=ALL-UNNAMED