モード変更


    言語

複数案件の経験から得た Java / Spring Boot バージョンアップ実践ガイド

2026/04/21

筆者はこれまで複数の案件で Java / Spring Boot のバージョンアップを担当してきました。Java 11 → 17 → 21、Spring Boot 2.x → 3.x → 3.4 と段階的にアップグレードしてきた中で、繰り返し遭遇する問題や、事前に知っておけばスムーズに進められるポイントが見えてきました。

本記事では、それらの経験を案件横断的に整理し、Spring Boot のバージョンアップに取り組むエンジニアの参考になる実践ガイドとしてまとめます。

対象読者

  • Java / Spring Boot で開発されたプロジェクトのバージョンアップをこれから実施する、または検討しているエンジニア
  • 特に Spring Boot 2.x → 3.x の大型マイグレーションに不安がある方

バージョンアップの進め方

大前提:Java バージョンの確認

Spring Boot 3.x は Java 17 以上が必須です(Spring Boot 2.x は Java 8 以上)。Java 8 や 11 を使用している場合は、Spring Boot のバージョンアップに先立って Java 17 以上にアップグレードする必要があります。筆者の案件でも先に Java をバージョンアップしてから Spring Boot のバージョンアップに着手しました。

なお、IntelliJ IDEA を使用している場合は、JDK のインストールだけでなく、プロジェクト構造の設定(ファイル → プロジェクト構造 → プロジェクト SDK)も新しい Java バージョンに変更する必要があります。これを忘れると、Java のバージョンが合わないというエラーメッセージが大量に出力されます。

大前提:テストコードを充実させる

バージョンアップ作業に着手する前に、テストコードが十分なカバレッジで整備されていることを確認してください。

本記事で推奨する「段階的なバージョンアップ」では、マイナーバージョンを1つ上げるたびにテストを実行してデグレードがないことを確認します。テストコードが不十分だと、各ステップで手動テストを実施することになり、テストだけで大きなコストがかかってしまいます。十分なカバレッジがない場合は、バージョンアップの前にテストコードの実装を優先することをお勧めします。

段階的にバージョンを上げる

Spring Boot のバージョンアップでは、一気に最新バージョンまで上げるのではなく、マイナーバージョン単位で段階的に上げていくことを強くお勧めします。

実は、筆者が最初にバージョンアップ作業を担当した案件では、Spring Boot 2.3 からいきなり当時の最新である 3.1 にバージョンアップし、発生したエラーを1つずつ対処していくという進め方をしていました。

  1. 2.3.x → 3.1.x(一気にメジャーバージョンアップ)
  2. 3.1.x → 3.2.x
  3. 3.2.x → 3.3.x
  4. 3.3.x → 3.4.x

この進め方だと、発生したエラーがどのバージョンのどの変更に起因するものなのか調査に時間がかかりました。また、事前に OpenRewrite で対応しているレシピを調べておけば手動で修正する工数も削減できたはずです。

その反省を踏まえ、以降の案件ではマイナーバージョンを1つずつ上げる方式に切り替えました。実際に 3.1 → 3.2 のステップではエラーが発生しましたが、3.2 → 3.3 ではエラーが一切発生しなかったケースもあり、各ステップでのエラー原因の特定が格段にしやすくなりました。

Release Notes を事前に調査する

各バージョンの Release Notes には、破壊的変更や非推奨化された API が記載されています。バージョンアップ作業に着手する前に、対象バージョン間の Release Notes をすべて確認しておくことで、影響範囲を事前に把握できます。Release Notes は Spring Boot の GitHub Wiki にまとめられています。また、メジャーバージョンアップ(2.x → 3.x 等)については Migration Guide も用意されているので、あわせて参照してください。

例えば、3.3 → 3.4 のアップグレードでは、Release Notes を事前に確認したことで @MockBean の非推奨化や ClientHttpRequestFactory 周りの変更を事前に把握でき、スムーズに対応できました。

ただし、Release Notes にすべての変更点が記載されているわけではありません。筆者の経験でも、RestTemplateBuilder の setConnectTimeout の非推奨化が Release Notes の非推奨一覧に記載されていなかったケースがありました。Release Notes の事前調査で影響範囲を大幅に絞り込むことはできますが、最終的にはテストを実行して初めて気づく変更点もあるという前提で臨んでください。

OpenRewrite による自動修正の活用

OpenRewrite は、コードの自動リファクタリングツールです。Spring Boot のバージョンアップ用のレシピが用意されており、機械的な修正を自動化できます。

筆者がある案件で Spring Boot 2.3 → 3.5 のアップグレードに OpenRewrite を利用したところ、以下のような修正が自動的に行われました。

  • javax.* → jakarta.* のパッケージ名変更
  • MySQL JDBC ドライバの mysql:mysql-connector-java → com.mysql:mysql-connector-j への変更
  • @MockBean → @MockitoBean への移行
  • HandlerInterceptorAdapter の extends → HandlerInterceptor の implements への変更
  • application.yml のプロパティ名変更(spring.datasource.initialization-mode → spring.sql.init.mode)

上記は筆者が実際に自動修正された項目ですが、これ以外にも Spring Security の設定変更(WebSecurityConfigurerAdapter → SecurityFilterChain、Lambda DSL 移行等)や Apache HttpClient 4 → 5 の移行など、本記事で紹介する多くの修正に対応するレシピが用意されています。

現在は AI を活用して同様の機械的な修正を行うこともできますが、OpenRewrite はフレームワークの移行ルールに基づいて正確に修正を行うため、修正漏れや誤変換のリスクが低く安心感があります。ただし、プロジェクト固有の実装やカスタマイズ部分は手作業での対応が必要になるため、自動修正後の確認は必須です。

IntelliJ IDEA 2024.1 Ultimate 以降では OpenRewrite プラグインが利用可能で、ビルドファイル(pom.xml / build.gradle)を開くとエディタ上にアイコンが表示され、そこからレシピを選択して実行できます。コマンドラインで実行するよりも手軽に利用できるのでお勧めです。

注意: IntelliJ IDEA 2026.1 では Java プラグインのモジュール分割等の大規模な破壊的変更が複数同時に入った影響で、2026年4月時点では OpenRewrite プラグインが未対応となっています。

ビルドツールのバージョンアップ

Spring Boot のバージョンアップに伴い、Gradle や Maven もバージョンアップが必要です。

Spring Initializr で対象の Spring Boot バージョンを指定してテンプレートプロジェクトを生成し、そのプロジェクトに含まれるビルドツールの設定を参照するのが確実な方法です。Gradle の場合は gradle/ フォルダをコピーし、Maven の場合は maven-wrapper.properties のバージョンを合わせます。筆者はこの方法で Gradle 6.x → 8.x、Maven 3.6 → 3.9 へのバージョンアップを複数案件で実施しました。

また、Gradle のメジャーバージョンアップでは構文の変更にも対応が必要です。Gradle 7 で compile が非推奨となり、Gradle 8 で完全に削除されたため、implementation に書き換える必要があります。

// Before
compile 'org.springframework.boot:spring-boot-starter-web'

// After
implementation 'org.springframework.boot:spring-boot-starter-web'

同様に、CheckStyle や Jacoco の reports ブロックで .enabled <boolean> が廃止され、.required = <boolean> に変更する必要があります。

// Before
reports {
    xml.enabled true
    html.enabled false
}

// After
reports {
    xml.required = true
    html.required = false
}

Spring Boot 2.x → 3.x の変更点

Spring Boot 2.x → 3.x は、Java EE → Jakarta EE の移行を含む大きな変更です。まずは公式の Migration Guide に記載されている変更点のうち筆者が実際に対処が必要だったものを紹介し、その後に Migration Guide には記載されていないものの遭遇した変更点を紹介します。

公式 Migration Guide に記載されている変更

以下は Spring Boot 3.0 Migration Guide や Spring Security 6.0 Migration Guide に記載されている変更点のうち、筆者が実際に対処が必要だったものです。

Jakarta EE への移行(javax → jakarta)

Spring Boot 3.0 で Java EE から Jakarta EE に移行したことにより、パッケージ名が javax.* から jakarta.* に変更されました。これはほぼすべてのプロジェクトで影響が発生します。

// Before
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;

// After
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotNull;

影響範囲が広いものの、修正自体は機械的なので、IDE の一括置換や OpenRewrite を利用すると効率的です。

MySQL JDBC ドライバの変更

Spring Boot 3.0 で MySQL JDBC ドライバの座標が mysql:mysql-connector-java から com.mysql:mysql-connector-j に変更されました。

あわせて確認しておきたいのが、接続 URL のタイムゾーン指定です。serverTimezone=JST のようなタイムゾーン略称は、公式ドキュメントで非推奨とされています。現時点では内部マッピングにより JST は Asia/Tokyo に解決されますが、将来のバージョンでマッピングが削除される可能性もあるため、serverTimezone=Asia/Tokyo のように IANA タイムゾーン名で指定しておくことをお勧めします。

Spring Security の設定方法の変更

WebSecurityConfigurerAdapter が削除されたため、SecurityFilterChain を @Bean として登録する方式に移行する必要があります。筆者が担当したすべての案件でこの対応が必要でした。

// Before
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated();
    }
}

// After
@EnableMethodSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated()
        );
        return http.build();
    }
}

主な変更点は以下の通りです。

BeforeAfter
extends WebSecurityConfigurerAdapter削除(POJO クラスに)
configure(HttpSecurity http) のオーバーライドSecurityFilterChain を @Bean 登録
authorizeRequests()authorizeHttpRequests()
antMatchers()requestMatchers()
and() によるメソッドチェーンLambda DSL(引数にラムダ式を渡す方式)
@EnableGlobalMethodSecurity@EnableMethodSecurity(prePostEnabled はデフォルトで true のため、明示的な指定は不要)

Apache HttpClient 4 → 5 への移行

Spring Framework 6.0 以降では Apache HttpComponents 5.1 以降が必須となりました。build.gradle に依存関係を追加した上で、import 文やクラス名を変更する必要があります。

// build.gradle に追加
implementation 'org.apache.httpcomponents.client5:httpclient5'

パッケージ名が org.apache.http.* から org.apache.hc.client5.http.* に変わりますが、クラス名やメソッド名も変更されているため、単純な文字列置換では対応できません。OpenRewrite には Apache HttpClient 5 への移行用の充実したレシピが用意されているので、利用できる場合は活用すると良いでしょう。手作業で移行する場合は 公式の Migration Guide を参照してください。

なお、RestTemplate は既にメンテナンスモードになっており、Spring Boot 3.2 以降では RestClient への移行が推奨されています。Apache HttpClient を直接利用している箇所が多い場合は、バージョンアップのタイミングで RestClient への移行も検討すると良いでしょう。

URL マッチングのデフォルト変更

Spring Boot 3.0 では URL マッチングに関して2つの大きな変更がありました。

1. デフォルトの URL マッチングクラスが AntPathMatcher から PathPattern に変更

PathPattern では AntPathMatcher で許容されていた一部のパターンが使えなくなります。例えば、/link/{.*} のようなパターンは PathPattern では構文エラーとなります。

移行先はコントローラでキャプチャ変数を使用しているかどうかで異なります。

  • キャプチャ変数を使用していない場合 → /link/** で OK
  • @PathVariable でキャプチャした値を使用している場合 → /link/{*path} が正しい移行先

なお、{*path} は AntPathMatcher の {path:.*} と異なり、キャプチャされる値の先頭に / が付く点に注意が必要です。

2. URL 末尾のスラッシュを無視しないようデフォルトの挙動が変更

これまで /api/users と /api/users/ は同じエンドポイントとしてマッチしていましたが、Spring Boot 3.0 からはデフォルトでは別のパスとして扱われます。

この変更は段階的に進んでおり、対応方法も変遷しています。

バージョン状況
Spring Boot 3.0 (Spring Framework 6.0)デフォルトで末尾スラッシュを区別するよう変更。PathMatchConfigurer.setUseTrailingSlashMatch(true) で元の挙動に戻せるが、同時にこのメソッドが非推奨化
Spring Boot 3.4 (Spring Framework 6.2)代替手段として UrlHandlerFilter が導入。末尾スラッシュありの URL をスラッシュなしの URL にリダイレクトする仕組み
Spring Boot 4.0 (Spring Framework 7.0)setUseTrailingSlashMatch メソッドが完全に削除

最終的に UrlHandlerFilter に移行する必要がありますが、これはパス単位でリダイレクトルールを設定する必要があるため、すべてのパスを末尾スラッシュなしに統一しておく必要があります。詳細は Spring Framework のドキュメント を参照してください。

筆者が実際に遭遇したその他の変更

以下は公式の Migration Guide には記載されていないものの、筆者が実際の案件で遭遇した変更点です。

ErrorController の getErrorPath() メソッド削除

ErrorController インターフェイスの getErrorPath() メソッドは Spring Boot 2.3 で非推奨となり、2.5 で削除されました。2.x の段階で既に削除されていますが、筆者のように 2.3 から一気に 3.x にバージョンアップした場合はこの変更に遭遇します。

代替として server.error.path プロパティで設定する方式に移行されています。getErrorPath() の戻り値が "/error"(デフォルト値と同じ)であった場合は、単にメソッドを削除するだけで対応できます。

HandlerInterceptorAdapter の廃止

HandlerInterceptorAdapter 抽象クラスが削除されたため、HandlerInterceptor インターフェイスを直接 implements するよう変更します。

// Before
public class MyInterceptor extends HandlerInterceptorAdapter {

// After
public class MyInterceptor implements HandlerInterceptor {

メソッドのシグネチャは変わらないため、修正は extends → implements への変更だけで済みます。

HttpStatus → HttpStatusCode

Spring Framework 6 から、一部の API でメソッドの引数や戻り値の型が HttpStatus(enum)から HttpStatusCode(インターフェイス)に変更されました。

// Before
public ResponseEntity<ErrorResponse> handleException(HttpStatus status) {

// After
public ResponseEntity<ErrorResponse> handleException(HttpStatusCode status) {

Bean 循環参照のデフォルト禁止

Spring Boot 2.6 から Bean の循環参照がデフォルトで禁止されました。2.x 時代にこの設定を有効化していなかった場合、3.x へのアップグレード時にエラーが発生します。spring.main.allow-circular-references=true で一時的に回避できますが、根本的には循環参照を解消すべきです。

@Async メソッドの戻り値制約の厳格化

Spring Framework 6.0 から、@Async を付与したメソッドの戻り値は void か Future でなければならないという制約が厳格に適用されるようになりました。これまで暗黙的に許容されていた他の戻り値型を使っていた場合、実行時にエラーが発生します。

Thymeleaf のフラグメント式の非推奨化

Spring Boot 3.0 に含まれる Thymeleaf 3.1 から、~{} で囲わないフラグメント式が非推奨となりました。単体テストでは検出されず、ステージング環境での手動テストで初めて大量の WARN ログとして発覚するケースがありました。

<!-- Before -->
<div th:replace="fragments/header :: header"></div>

<!-- After -->
<div th:replace="~{fragments/header :: header}"></div>

詳細は Thymeleaf 3.1: What's new and how to migrate の 2.6 を参照してください。

application.yml のプロパティ名変更

Spring Boot 2.5 からデータソース初期化に関するプロパティ名が変更されています。2.x 時代は非推奨の警告のみでしたが、3.x では旧プロパティ名が認識されなくなるため、変更が必須です。

# Before
spring.datasource.initialization-mode: always

# After
spring.sql.init.mode: always

この他にも複数のプロパティ名が変更されています。詳細は Spring Boot 2.5 Release Notes を参照してください。

Spring Boot 3.x 内のマイナーバージョンアップで注意すべき変更

2.x → 3.x のメジャーバージョンアップに比べると変更は小さいですが、マイナーバージョンアップでも注意が必要な変更があります。

Spring Boot 3.2: NoResourceFoundException の導入

Spring Framework 6.1(Spring Boot 3.2)から、静的リソースが見つからない場合に ResourceHttpRequestHandler が NoResourceFoundException を throw するようになりました。従来 ErrorController でカスタマイズしていた 404 レスポンスが正しく返らなくなるケースがあります。この場合は ResponseEntityExceptionHandler の handleNoResourceFoundException メソッドをオーバーライドして対応します。

Spring Boot 3.2: SimpleClientHttpRequestFactory のリクエストバッファリング非推奨化

同じく Spring Framework 6.1(Spring Boot 3.2)から、SimpleClientHttpRequestFactory の setBufferRequestBody メソッドが非推奨かつ削除予定となりました。それだけでなく、メソッドの実装自体が空になっており、設定しても効果がありません。

setBufferRequestBody(false) でバッファリングを無効化していた場合は、そもそも設定が不要になったため、呼び出し箇所を単に削除するだけで対応できます。一方、setBufferRequestBody(true) でバッファリングを有効化していた場合は、使用している ClientHttpRequestFactory を BufferingClientHttpRequestFactory でラップする方法が 公式に推奨 されています。

ClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
ClientHttpRequestFactory bufferingFactory = new BufferingClientHttpRequestFactory(factory);
RestTemplate restTemplate = new RestTemplate(bufferingFactory);

Spring Boot 3.3: Flyway 10 のモジュール分割

Spring Boot 3.3 で Flyway 10 に対応したことにより、データベース固有の機能が個別のモジュールに分離されました。使用しているデータベースに応じて、対応するモジュールを依存関係に追加する必要があります。

<!-- PostgreSQL の場合 -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-postgresql</artifactId>
</dependency>

Spring Boot 3.4: @MockBean / @SpyBean の非推奨化

Spring Boot 3.4 で @MockBean と @SpyBean が非推奨となり、代わりに @MockitoBean と @MockitoSpyBean を使うよう変更が必要です。

// Before
@MockBean
private UserService userService;

// After
@MockitoBean
private UserService userService;

アノテーション名の置換だけで対応できます。

Spring Boot 3.4: @ConfigurationProperties のネストクラスに @Valid が必要に

Spring Boot 3.4 から、@ConfigurationProperties を付与したクラス内のネストしたクラスに Bean Validation のアノテーション(@NotNull 等)が使用されている場合、そのネストクラスに対応するフィールドに @Valid を付与する必要があります。

@ConfigurationProperties(prefix = "app")
public record AppProperties(
    @Valid Notification notification  // 3.4 から @Valid が必要
) {
    public record Notification(
        @NotNull String endpoint,
        @NotNull Integer retryCount
    ) {}
}

@Valid が付与されていないと、ネストクラス内のバリデーションが実行されなくなります。

Spring Boot 3.4: RestTemplateBuilder / ClientHttpRequestFactory の変更

Spring Boot 3.4 では、RestTemplateBuilder の setConnectTimeout メソッドが非推奨となり、新設の connectTimeout メソッドを使用するよう変更されました。

// Before
restTemplateBuilder.setConnectTimeout(Duration.ofSeconds(5))

// After
restTemplateBuilder.connectTimeout(Duration.ofSeconds(5))

なお、この変更は Release Notes の非推奨一覧には記載されておらず、ClientHttpRequestFactory Builders の項目に関連して行われた変更でした。Release Notes だけでなく、実際の JavaDoc も確認することをお勧めします。

また、ClientHttpRequestFactorySettings.DEFAULTS も非推奨となり、ClientHttpRequestFactoryBuilder への移行が必要です。ここで移行先の選択を間違えやすいので注意が必要です。

ClientHttpRequestFactoryBuilder には simple()、jdk()、httpComponents() 等のファクトリメソッドがありますが、これらは全く異なる HTTP クライアント実装に対応しています。

メソッドHTTP クライアント実装
simple()java.net.HttpURLConnection(Java 1.1 からある旧 API)
jdk()java.net.http.HttpClient(Java 11 で導入された新 API、HTTP/2 対応)
httpComponents()Apache HttpClient 5

ClientHttpRequestFactorySettings.DEFAULTS は、クラスパス上のライブラリを検出して以下の優先順位でファクトリを選択していました。

  1. Apache HttpComponents がクラスパスにあれば → HttpComponentsClientHttpRequestFactory
  2. Jetty がクラスパスにあれば → JettyClientHttpRequestFactory
  3. Reactor Netty がクラスパスにあれば → ReactorClientHttpRequestFactory
  4. JDK HttpClient(Java 11+)→ JdkClientHttpRequestFactory
  5. いずれもなければ → SimpleClientHttpRequestFactory(HttpURLConnection ベース)

したがって、DEFAULTS からの移行先は、クラスパスの状態に応じて適切なメソッドを選ぶ必要があります。特に Apache HttpComponents も Jetty も使用していないプロジェクトでは simple() が正しい移行先であり、jdk() ではありません。

筆者が担当した案件では DEFAULTS を jdk() に移行したところ、外部 SaaS への HTTP リクエストが 2 回に 1 回タイムアウトするという問題が発生しました。java.net.http.HttpClient はデフォルトで HTTP/2 のネゴシエーションを試みるため、相手サーバーとの間で接続確立に問題が起きていたものと考えられます。simple() に変更したところ問題は解消しました。

筆者が経験したライブラリ移行

Spring Boot のバージョンアップは、依存ライブラリの移行が必要になる良い機会でもあります。以下は筆者が実際の案件で対応したライブラリ移行です。

springfox → springdoc-openapi

springfox は 3.0.0(2020年リリース)を最後に長期間更新されておらず、Spring Boot 3.x では動作しません。@EnableSwagger2 を残したままにすると javax.servlet.http.HttpServletRequest が見つからないというエラーが発生します。

代替として springdoc-openapi への移行が推奨されています。

// Before
implementation 'io.springfox:springfox-boot-starter:3.0.0'

// After
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.4'

Swagger の設定クラスがカスタマイズされていない場合は、設定クラスごと削除して springdoc-openapi のデフォルト設定を使うことで移行コストを下げられます。

AWS SDK v1 → v2

AWS SDK for Java v1 は 2025年12月31日で End of Support を迎えました。v2 への移行が必須ですが、API の設計が v1 とは大きく異なるため(特に DynamoDB)、影響範囲が大きくなりがちです。

DynamoDB を利用している場合は、AWS SDK v2 をそのまま使うよりも Spring Cloud AWS の利用を検討すると良いでしょう。DynamoDbTemplate が利用可能になる等の利便性向上に加え、テーブル名 prefix 対応などの実用的な機能も提供されています。

GSON → Jackson

Spring Boot には Jackson が標準で含まれているため、GSON と併用する意味はほとんどありません。さらに、Java 17 以降の JDK 内部要素のカプセル化(JEP 403)により、GSON がリフレクションで内部クラスにアクセスして InaccessibleObjectException が発生するケースがあります。

java.lang.reflect.InaccessibleObjectException: Unable to make field
private final int java.time.LocalDate.year accessible: module java.base
does not "opens java.time" to unnamed module

このエラーが発生した場合は、GSON を Jackson に置き換えることで解決できます。バージョンアップを機に、JSON ライブラリを Jackson に統一することをお勧めします。

jjwt のメジャーバージョンアップ

jjwt は 0.9.x → 0.12.x でメソッド名や依存ライブラリの構成が大きく変わっています。まとまったマイグレーションガイドが公式には存在しないため、以下に主要な変更点を記載します。

依存関係の変更

// Before
implementation 'io.jsonwebtoken:jjwt:0.9.1'

// After
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'

トークン作成の変更

// Before
String token = Jwts.builder()
    .setIssuedAt(issuedAt)
    .setExpiration(expiration)
    .setSubject(subject)
    .signWith(SignatureAlgorithm.HS512, secretKey)
    .compact();

// After
String token = Jwts.builder()
    .issuedAt(issuedAt)
    .expiration(expiration)
    .subject(subject)
    .signWith(Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey)), Jwts.SIG.HS512)
    .compact();

トークン検証の変更

// Before
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);

// After
Jwts.parser()
    .verifyWith(Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey)))
    .build()
    .parseSignedClaims(token);

また、io.jsonwebtoken.SignatureException が非推奨となり、io.jsonwebtoken.security.SignatureException に移動しています(後者は前者のサブクラスなので、import 文の変更だけで対応可能)。

更新が停止しているライブラリの対処

バージョンアップは、長期間更新されていないライブラリを見直す良い機会です。筆者が遭遇した例を紹介します。

  • jBCrypt(最終更新 2020年)→ spring-security-crypto に含まれる BCryptPasswordEncoder に移行。jBCrypt が必要なプロジェクトでは Spring Security も使用している可能性が高いため大抵の場合 spring-boot-starter-security に含まれる形で利用できるが、筆者が担当したあるサーバーでは例外的に Spring Security を使用していなかった。その場合、spring-boot-starter-security を追加すると AutoConfiguration により認証・認可の挙動が変化してしまうため、暗号化機能だけを利用するために spring-security-crypto のみを依存関係に追加する必要があった
  • javafaker(最終更新 2020年2月)→ 調査の結果、コメントアウトされたコードでしか使われていなかったため削除

また、Spring Boot が管理しているライブラリ(Lombok、Jackson、Spring Security Test 等)に個別にバージョンを指定している場合は、バージョン指定を削除して Spring Boot の依存関係管理に任せるようにしましょう。

Java バージョンアップに伴う注意点

Spring Boot のバージョンアップと同時に Java のバージョンも上げる場合、Java 側の変更に起因するエラーにも対応が必要です。

JDK 内部要素のカプセル化(JEP 403, Java 17〜)

Java 17 から JEP 403: Strongly Encapsulate JDK Internals により、JDK 内部の API へのリフレクションによるアクセスがデフォルトで禁止されました。

筆者が遭遇したケースでは、Apache Commons Lang の ReflectionToStringBuilder.toString() メソッドが以下のエラーを発生させました。

java.lang.reflect.InaccessibleObjectException: Unable to make field
static final int java.util.HashMap.DEFAULT_INITIAL_CAPACITY accessible:
module java.base does not "opens java.util" to unnamed module

--add-opens JVM オプションで回避することも可能ですが、根本的にはリフレクションに依存しないコードに修正することが望ましいです。実際に上記のケースでは ReflectionToStringBuilder の出力内容自体がデバッグに有用でない内容(java.util.HashMap@535f6e95 のような文字列)だったため、効果的なログが出力されるよう実装を見直しました。

Mockito の動的エージェントロード警告(Java 21〜)

Java 21 以降では、Mockito が動的にエージェントをロードする際に以下のような警告が出力されます。

WARNING: A Java agent has been loaded dynamically
WARNING: Dynamic loading of agents will be disallowed by default in a future release

Maven の場合は、maven-surefire-plugin の設定で Mockito をエージェントとして指定します。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>properties</goal>
            </goals>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine>
    </configuration>
</plugin>

Gradle の場合は、test タスクの jvmArgs に設定します。詳細は Mockito の公式ドキュメント を参照してください。

バージョンアップ時に役立つ実践的な Tips

不要な依存関係の棚卸し

バージョンアップは依存関係を見直す良い機会です。筆者が複数案件で実施した棚卸しでは、以下のようなケースが見つかりました。

  • 使用されていないライブラリ:build.gradle / pom.xml に記載されているが、実際のコードで使われていないもの。削除してテストが通れば不要
  • Spring Boot が管理しているライブラリの個別バージョン指定:spring-boot-starter-* に含まれるライブラリのバージョンを個別に固定している場合、その指定を削除して Spring Boot の BOM に任せる
  • 過去の残骸:別のライブラリに移行済みなのに旧ライブラリが残っているケース

Error Prone の新チェックへの対応

Error Prone を利用している場合、バージョンアップに伴い新しいチェックが有効化されることがあります。筆者が遭遇した例と、その際にとった対応を以下に記載しますが、@SuppressWarnings による抑制はあくまで暫定的な対応であり、可能であればチェックの指摘に従ってコードを改善する方が望ましいです。

チェック内容筆者がとった対応
NullablePrimitiveプリミティブ型に @NotNull が付与されている無意味な @NotNull を削除
EnumOrdinalordinal() の使用を検出既存の動作への影響を考慮し @SuppressWarnings で抑制
StatementSwitchToExpressionSwitchswitch 文を switch 式に変換可能switch 式に書き換え
IntLiteralCastint リテラルを long にキャストリテラルに L サフィックスを付与
UnicodeInCodeコード中に非 ASCII 文字テストメソッド名の日本語に対して @SuppressWarnings で抑制

EnumOrdinal や UnicodeInCode については @SuppressWarnings で対応しましたが、本来は ordinal() を使わない実装への修正や、テストメソッド名の命名規則の見直しが理想的です。バージョンアップのスコープとの兼ね合いで判断してください。

commons-logging の競合

AWS SDK v1 や Firebase Admin SDK など、commons-logging を推移的に依存として含むライブラリがクラスパスにあると、Spring Boot 3.x の spring-jcl と競合して起動に失敗することがあります。

Standard Commons Logging discovery in action with spring-jcl:
please remove commons-logging.jar from classpath in order to avoid potential conflicts

該当のライブラリから commons-logging を除外することで解決します。

// Gradle
implementation('com.amazonaws:aws-java-sdk') {
    exclude group: 'commons-logging', module: 'commons-logging'
}

なお、AWS SDK v2 には commons-logging が含まれないため、v2 に移行すればこの問題は自然に解消されます。

まとめ

本記事では、筆者が複数案件で Java / Spring Boot のバージョンアップを実施した経験から得た知見を整理しました。ポイントを振り返ります。

  • テストコードを充実させてからバージョンアップに着手する。段階的なバージョンアップの各ステップで自動テストにより確認できる体制が大前提
  • 段階的にバージョンを上げることで、エラーの原因特定が容易になる
  • Release Notes の事前調査が最も効率的な準備。実際に影響がある変更を事前に把握できる
  • OpenRewrite を活用して、javax → jakarta 等の機械的な修正を自動化する
  • バージョンアップは依存関係の棚卸しの好機。使われていないライブラリの削除や、更新が停止したライブラリの代替への移行を実施する

Spring Boot 2.x → 3.x はJakarta EE 移行を含む大きな変更ですが、段階的に進めれば一つ一つの対応は難しくありません。本記事が、これからバージョンアップに取り組む方の参考になれば幸いです。

続編について

現在、Java 17/21 → 25 および Spring Boot 3.x → 4.x へのアップデートにも取り組んでいます。Spring Boot 4.0 は Spring Framework 7 ベースであり、本記事で触れた setUseTrailingSlashMatch メソッドの完全削除をはじめ、さらに大きな変更が入っています。その知見も別の記事にまとめる予定です。

参考リンク

  • Spring Boot 3.0 Migration Guide
  • Spring Security 6.0 Migration Guide
  • Spring Boot 3.3 Release Notes
  • Spring Boot 3.4 Release Notes
  • Apache HttpClient 5.x Migration Guide
  • OpenRewrite - Spring Boot 3.x Migration
  • Spring Framework URL Handler Filter ドキュメント
  • AWS SDK for Java v1 End of Support
javaspring bootspring securitymigrationgradlemavenversion upgradeopenrewrite

Author

Noboru Mitsuishi

Noboru Mitsuishi

Backend Engineer

Java & Spring Boot enthusiast

その他おすすめ記事

2026/04/16

AIとの壁打ちに飽きた人たちへ

AIでコードを書くのは速くなった。一方で、仕様決めのAI壁打ちには少し飽きてきた。同じように感じている人、いませんか? 「これどう思う?」と聞くと丁寧に答えてくれるし、「他の案は?」と聞けば、3つくらい出してくれる。非常に便利になったと理解しています。 でも、なんだろう、このインタビューしてる感。自分がずっと聞き手で、AIがずっと回答者。1対1の問答がずっと続く。 この記事では、BMAD-METHODと呼ばれる、ディスカッション形式でAI駆動のアジャイル開発を進めるOSSを活用し、これらの課題を解決する方...

Yohei Fukuma

Yohei Fukuma

ChatGPT

2026/03/26

リモートワークからリモートAIワークへ

いつの間にか、誰も議事録を書かなくなっていました。 Google Meet で Gemini が自動的に文字起こしをして、会議が終わると要約とアクションアイテムを出してくれます。気づいたら、チームの誰もがそれを当たり前のように使っていました。それを読むかどうかは人それぞれです。ただ、『会議で話したことが5分後に要約されたテキストになる』という事実は、最初に見たとき素直に驚きました。 この変化は、働き方の大きな移行の入り口だったと思います。私はこの新たな働き方を、「リモートAIワーク」と定義しました。 この...

Jinma Yamashita

Jinma Yamashita

ChatGPT

サービス開発実績会社情報
採用情報インサイトお問い合わせ
© 2022 Monstarlab
情報セキュリティ基本方針個人情報の取り扱いについて個人情報保護方針