メインコンテンツまでスキップ

Spring Boot 4.0 移行ガイド

概要

Spring Boot 3.5.6 → 4.0.6 + Jackson 2 → 3 への移行で発生した問題と対応をまとめる。

バージョン変更

コンポーネントBeforeAfter
Spring Boot3.5.64.0.6
Spring Framework6.x7.x
Spring Security6.x7.x
Jackson2.14.23.1.2
Servlet API6.0 (Tomcat 10.x)6.1 (Tomcat 11)
dependency-management plugin1.1.41.1.7

1. Jackson 2 → 3 移行

公式ガイド: Migrating to Jackson 3 | Jackson 3.0.0 Release Notes | Jackson 3 in Spring Boot 4 | Spring の Jackson 3 サポート

1.1 パッケージ変更

BeforeAfter備考
com.fasterxml.jackson.databind.ObjectMappertools.jackson.databind.json.JsonMapperimmutable builder pattern
com.fasterxml.jackson.core.JsonProcessingExceptiontools.jackson.core.JacksonExceptionunchecked exception
com.fasterxml.jackson.databind.JsonNodetools.jackson.databind.JsonNode
com.fasterxml.jackson.databind.node.JsonNodeFactorytools.jackson.databind.node.JsonNodeFactory
com.fasterxml.jackson.databind.PropertyNamingStrategiestools.jackson.databind.PropertyNamingStrategies
com.fasterxml.jackson.annotation.*変更なしannotations は共有

1.2 API 変更

ObjectMapper → JsonMapper (Builder Pattern)

// Before (Jackson 2)
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
objectMapper.registerModule(new JavaTimeModule());

// After (Jackson 3)
JsonMapper jsonMapper = JsonMapper.builder()
.changeDefaultVisibility(vc ->
vc.withVisibility(PropertyAccessor.ALL, Visibility.NONE)
.withVisibility(PropertyAccessor.FIELD, Visibility.ANY))
.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
// JavaTimeModule は不要(Jackson 3 に内蔵)
.build();

Coercion 設定

// Before (Jackson 2)
objectMapper
.coercionConfigFor(LogicalType.Collection)
.setCoercion(CoercionInputShape.String, CoercionAction.AsNull);

// After (Jackson 3)
JsonMapper.builder()
.withCoercionConfig(LogicalType.Collection, config -> {
config.setCoercion(CoercionInputShape.String, CoercionAction.AsNull);
})
.build();

JsonNode API 変更

Before (Jackson 2)After (Jackson 3)
jsonNode.fieldNames()jsonNode.properties()Set<Map.Entry<String, JsonNode>>
jsonNode.elements()jsonNode.iterator()

jackson-datatype-jsr310

Jackson 3 では java.time サポートが内蔵されたため、jackson-datatype-jsr310 依存は不要。

1.3 private final フィールド問題(重要)

Jackson 3 では private final フィールドへのリフレクション書き込みができなくなった。

Jackson でデシリアライズされるクラス(Redis キャッシュ経由、JSON → Object 変換等)の private final フィールドを private に変更する必要がある。

// Before - Jackson 3 でデシリアライズ失敗
public class SecurityEventLogConfiguration {
private final boolean persistenceEnabled; // ← final があるとデフォルト値のまま
}

// After - Jackson 3 でデシリアライズ成功
public class SecurityEventLogConfiguration {
private boolean persistenceEnabled; // ← final を外す
}

影響範囲: フィールドの型として使われるネストされたオブジェクトも含めて、デシリアライズ対象のオブジェクトグラフ全体で private final を確認する必要がある。

対象クラスの例:

  • テナント設定クラス(SecurityEventLogConfiguration, SessionConfiguration, CorsConfiguration, UIConfiguration 等)
  • セッション関連値オブジェクト(OPSessionIdentifier, BrowserState 等)
  • Redis キャッシュ経由でシリアライズ/デシリアライズされる全オブジェクト

1.4 FAIL_ON_UNKNOWN_PROPERTIES のデフォルト変更(重要)

Jackson 3 ではデシリアライズ時の未知フィールドの扱いが Jackson 2 と逆になった。

バージョンデフォルト未知フィールド遭遇時
Jackson 2.xtrueUnrecognizedPropertyException を投げる
Jackson 3.xfalse黙って無視する

JsonConverter は明示的な設定をしていないため、Jackson 3 のデフォルト挙動(未知フィールドを無視)が適用される。

影響:

  • 設定 JSON や API リクエストの typo は実行時エラーにならない
  • 旧→新、新→旧 の双方向ロールバックで未知フィールドを許容できる(前方互換性向上)
  • Jackson 3.1.0 にはこの設定が @JsonIgnoreProperties と相互作用するバグがあり、3.1.2 で修正された

Jackson 2 互換の厳格挙動を維持したい場合は、JsonMapper.builder().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) を明示する必要がある。


2. Spring Boot 4.0 固有の変更

公式ガイド: Spring Boot 4.0 Migration Guide | Spring Boot 4.0 Release Notes | Spring Security 7 リファレンス

2.1 SessionAutoConfiguration 削除

Spring Boot 4.0 では SessionAutoConfiguration が削除された。

// Before
@SpringBootApplication(exclude = SessionAutoConfiguration.class)
public class IdPApplication { }

// After
@SpringBootApplication
public class IdPApplication { }

2.2 ContentCachingRequestWrapper

ContentCachingRequestWrapper のコンストラクタに maxContentLength 引数が必須になった。

// Before
new ContentCachingRequestWrapper(request);

// After
new ContentCachingRequestWrapper(request, 50000);

2.3 httpBasic の無効化

Spring Security 7 では httpBasic を明示的に無効化する必要がある場合がある。

http.httpBasic(AbstractHttpConfigurer::disable);

3. Tomcat 11 (Servlet 6.1) の変更

公式ガイド: Tomcat 11 Migration Guide | Servlet 6.1 Specification (Jakarta EE 11) | RFC 7230 Section 3.2 - Header Fields

3.1 ヘッダー名のケース保持

Tomcat 10.x では HttpServletRequest.getHeaderNames() がヘッダー名を小文字で返していたが、Tomcat 11 では原形のケースを保持するようになった。

Tomcat 10.x: "authorization"
Tomcat 11: "Authorization"

HTTP ヘッダー名は RFC 7230 で case-insensitive と定義されているため、ヘッダー名でMap検索する場合は case-insensitive で比較する必要がある。

// Before - Tomcat 10.x では動いたが Tomcat 11 で失敗
String authorization = headersMap.get("authorization");

// After - case-insensitive 検索
private String getHeaderValueCaseInsensitive(Map<String, String> headers, String name) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equalsIgnoreCase(name)) {
return entry.getValue();
}
}
return "";
}

注意: Spring MVC の @RequestHeader アノテーションは内部で case-insensitive 処理を行うため、この問題は発生しない。問題は HttpServletRequest.getHeaderNames() で取得したヘッダー名を自前で Map に格納し、後から文字列キーで検索するパターンで発生する。


4. 移行チェックリスト

build.gradle

  • Spring Boot バージョンを 4.0.x に更新
  • dependency-management プラグインを 1.1.7 に更新
  • Jackson 依存を tools.jackson.core:jackson-databind に変更
  • jackson-datatype-jsr310 依存を削除

Java コード

  • Jackson import 文を tools.jackson.* に変更(annotations は除く)
  • ObjectMapperJsonMapper (Builder Pattern) に変更
  • JsonProcessingExceptionJacksonException に変更
  • fieldNames()properties() に変更
  • elements()iterator() に変更
  • Coercion 設定を Builder API に変更
  • SessionAutoConfiguration の exclude を削除
  • ContentCachingRequestWrappermaxContentLength 引数を追加
  • Jackson でデシリアライズされるクラスの private finalprivate に変更
  • ヘッダー名の case-insensitive 検索を確認

テスト

  • ユニットテスト全通し
  • E2E テスト全通し
  • Redis キャッシュをフラッシュしてからテスト(古い形式のキャッシュデータが残ると問題)

5. 影響を受けないもの

  • @RequestHeader アノテーション経由のヘッダー取得(Spring MVC が case-insensitive 処理)
  • com.fasterxml.jackson.annotation.*(Jackson 2/3 共有)
  • MongoDB, Undertow, Hazelcast(未使用)
  • @MockBean / @SpyBean(未使用)

参考資料

Spring Boot / Spring Framework

Jackson

Tomcat / Servlet