1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SpringSessionにRedis+Jacksonを適用して、デシリアライズ時にオブジェクト不整合のエラーを回避する

Last updated at Posted at 2025-09-04

対応したことの3行まとめ

  • spring-session-data-redis を依存関係へ追加する
  • @EnableRedisIndexedHttpSession を設定に追加する
  • セッションへ格納するクラスに @JsonIgnoreProperties(ignoreUnknown = true) を追加する

セッション情報とは

Javaで作成するWebアプリケーションには、アプリケーション利用者ごとにデータを保持する「セッション情報1」があります。セッション情報を扱うことで、例えばログインした利用者ごとに入力情報を複数の画面にわたって保持する2のが簡単に実現できます。

Spring Frameworkのセッション拡張:SpringSession

SpringSessionはアプリケーションとセッション管理の間に抽象化レイヤーを提供します。SpringSessionではセッション情報の保存先にリレーショナルデータベース、NoSQLデータベースなどさまざまな永続ストアに保存できます。これにより分散アプリケーション環境でのセッション管理が実現できます。

また使用する永続ストアに関係なく同じAPIを通してセッションを管理できるため、用いる永続ストアに依ってアプリケーションのコードを変える必要がありません。

他にも、セッションの有効期限の設定や、異なるWebアプリケーション間のクロスコンテキスト通信などの機能も提供します。

まとめると、SpringSessionはWebアプリケーションでのユーザーセッションの管理を簡素化し、アプリケーションの構築に集中しやすくします。

SpringSessionが利用する永続ストア

SpringSessionが標準で対応している永続ストアには以下があります。

ストア 利用方法
Redis spring-session-data-redis の適用
リレーショナルデータベース リレーショナルデータベースごとのJDBC3ドライバー
MongoDB spring-session-data-mongodbの適用

選定については、すでに存在する永続ストアがあるかなど、コスト面や負荷状況から選ぶことになるでしょうか。接続する永続ストアを簡単に切り替えできるのもSpringSessionのいいところです。

SpringSessionの利用先にRedisを適用し、Redisへ保管する方法にJSONを選択する

かんたんに方法をまとめると

  • プロジェクトに org.springframework.session:spring-session-data-redis を適用する
  • Redis用のSpring設定を追加する

です。

spring-session-data-redis を適用する

gradle であれば、dependenciesspring-session-data-redis を追加します。

build.gradle
dependencies {
....
	implementation 'org.springframework.session:'
....
}

Redis用のSpring設定を追加する

複数の方法が用意されていますが、本記事ではコードで記述します。

以下のConfigurationクラスを追加します。他の設定用クラスに混ぜても構いません。
@EnableRedisIndexedHttpSession を追加し、RedisSerializer<Object> を返す springSessionDefaultRedisSerializer(ObjectMapper) メソッドを定義します。

@Configuration
@EnableRedisIndexedHttpSession(redisNamespace = "spring:session:spring-sample-session-serialize")
public class SessionConfig {
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(ObjectMapper objectMapper) {
        return new GenericJackson2JsonRedisSerializer("");
    }    
}

これでセッション情報をJSONに変換した内容でRedisへ保存します。

セッション格納方法にJSONを選択すると何がうれしいか

JavaのWebアプリケーションは、セッションへ格納していたクラスとアプリケーションの実装に違いがあると、アプリケーションがセッションから情報を参照した際には「型が違う」ためセッションが復元できずにアプリケーションエラーが発生します。
こうなると、セッション情報をすべて破棄するか、有効期限が切れるまではアプリケーションエラーが発生し続けることになります。

具体的な例)
セッションに格納していたクラス:

@Getter @Setter @NoArgsConstructor @AllArgsConstructor
public class UserInfo implements Serializable {
    private String userId;
    private String userName;
}

アプリケーション変更後のセッションに格納するクラス:

@Getter @Setter @NoArgsConstructor @AllArgsConstructor
public class UserInfo implements Serializable {
    private String userId;
    private String userName;
    private Integer age;    // フィールドの追加
}

今回のようにJacksonを使ってセッション情報を扱うと、JacksonObjectMapper でシリアライズ 4 し、セッションから取り出すときも JacksonObjectMapper がデシリアライズ5 します。

これにより、復元しようとした型が違うないしは存在しない場合の制御が Jackson の設定で行え、セッションエラーが回避されますので、アプリケーションエラーを起こすかどうかも実装できます。

セッションに格納するオブジェクトは
また、SpringSession+Redisのライブラリは、以下の形式でJSONに変換したオブジェクトと、セッションの有効期限に関する情報もあわせて格納します。

Redisに格納する内容

セッション情報そのものと、セッションの寿命に関する専用のフィールド名を自動的に使います。

フィールド 説明
sessionAttr:userProfile { "@class": "com.github.apz.sample.model.UserProfile", "userId": { "@class": "com.github.apz.sample.model.UserId", "value": "" }, "userName": { "@class": "com.github.apz.sample.model.UserName", "value": "" } } JacksonによりJSONへ変換したセッション格納オブジェクト
creationTime 1756363221869 セッション作成時刻
lastAccessedTime 1756363221869 セッションオブジェクトの最終アクセス時間
maxInactiveInterval 1800 セッションオブジェクトの有効期限

セッション情報を操作するアプリケーションはこのセッション情報を取り出すときは、このJSONで変換したオブジェクトとセッションの有効期限に関するフィールドを元に、有効期限内の値なのか、どのクラスにデシリアライズして値を格納するのかを決定します。

セッションの属性が増えるとき

Redisに保存されていたセッション情報に対してアプリケーション側で新しい属性が増えた場合、新しい属性はnullになります6。アプリケーションエラー等は発生しません。

セッションの要素が減ったとき

Redisに保存されていたセッション情報に対して、アプリケーションのセッション情報から属性が足りなかった場合は、jackson が 「JSONからオブジェクトを復元したときにフィールドが不足している」旨の例外をスローします。

org.springframework.data.redis.serializer.SerializationException: 
Could not read JSON:Unrecognized field "userAge" 
(class com.github.apz.sample.model.UserProfile), 
not marked as ignorable (2 known properties: "userId", "userName"])

上記のメッセージから、アプリケーションのUserProfileクラスは userId と userName の2つだが、JSONで格納されている userAge プロパティは認識できないので読めない です。まさしくその通りですね。

回避方法ですが、セッション情報に格納しているクラスに、デシリアライズ5する際に存在しないフィールドがあった場合には無視するようJacksonの設定を記述します。

@JsonIgnoreProperties(ignoreUnknown = true)
public class UserProfile implements Serializable {
  ......
}

以上です。

参考実装

参考文献

url 内容
https://spring.pleiades.io/spring-session/reference/ Spring Session 公式
https://www.baeldung.com/jackson-exception jacksonのエラーと対応のまとめ
  1. セッション情報はJavaのWebアプリケーションの仕様である サーブレット仕様 で扱います。
    サーブレット仕様:https://docs.oracle.com/cd/E18355_01/web.1013/B31859-01/overview.htm
    サーブレットセッション:https://docs.oracle.com/cd/E18355_01/web.1013/B31859-01/overview.htm#810230

  2. WebアプリケーションはHTTPで実現しています。HTTPはステートレス、つまりリクエストに対して簡単な処理を行った結果を出力し、すぐに消滅するものです。つまり情報を保持しません。
    これに対してセッションは、ログイン情報やショッピングサイトの買い物かごのように、複数の画面を遷移しても状態を維持する仕組みをHTTPで実現できるようにする仕様で、これはJavaのWebアプリケーションはサーブレット仕様に則って実装します。

  3. JDBC:https://docs.oracle.com/cd/G11854_01/jjdbc/introducing-JDBC.html リレーショナルデータベースへ接続し、データの送受信を行うモジュール。リレーショナルデータベースごとに用意される。

  4. オブジェクトを文字列に変換すること。日本語では「直列化」と表現します。

  5. 文字列からオブジェクトを復元すること。 2

  6. この動きはJacksonの設定で変わります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?