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.propertiesdistributionUrlを以下のように変更した

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