こんにちは。 エキサイト株式会社の三浦です。
Spring Boot
には様々な機能が用意されており、その一つに Redis
へのキャッシュ機能があります。
非常に便利なのですが、少なくとも Spring Boot
の ver.2.6.6
では、 List.of
(正確には、 List.of
などによって返却されるイミュータブルコレクション)等の一部の型でキャッシュが使えないという問題があります。
今回は、 List.of
を使えるようにする方法について説明していきます。
Spring BootとRedisキャッシュ
Spring Boot
では、spring-data-redisというライブラリを使って、任意のデータを Redis
にキャッシュすることができます。
便利なのですが、少なくとも Spring Boot ver.2.6.6
の段階では、 List.of
をキャッシュすると以下のようなエラーが出てしまいます。
Could not read JSON: Unexpected token (END_ARRAY), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.lang.Object)\n at [Source: (byte[])\"[]\"; line: 1, column: 2]; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (END_ARRAY), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.lang.Object)\n at [Source: (byte[])\"[]\"; line: 1, column: 2]
これは、 Redis
へのキャッシュ方法が原因となっています。
Redis
キャッシュの流れとして、
- キャッシュを作成する際は、Java用のデータをJSON化して
Redis
に保存- この際、Java側の型情報も一緒に保存
- キャッシュを取得する際は、
Redis
のデータをJSONからJava用のデータに変換して使用- この際、保存されている型情報も参照する
となっています。
List.of
などの final
なデータはJSON化はできるのですが、JSONからデータを戻す際に型情報の変換がうまく行かずエラーが起きてしまうようです。
List.ofをキャッシュする方法
これを解決するためには、JSONの変換周りをカスタマイズする必要があります。
Spring Boot
では、 Redis
キャッシュの値のシリアライザを開発者側で指定することができるのですが、それを以下のようにすれば、 List.of
をキャッシュできるようになります。
private GenericJackson2JsonRedisSerializer serializer() { // *1 ObjectMapper objectMapper = new ObjectMapper() .activateDefaultTyping( LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.EVERYTHING, JsonTypeInfo.As.PROPERTY ); // *2 // GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, null); return new GenericJackson2JsonRedisSerializer(objectMapper); }
少し解説していくと、
*1
基本的にはデフォルト設定と同じですが、第二引数である DefaultTyping.EVERYTHING
が変わっています。
これを第二引数に入れることで、 final
なものであっても型情報を保存するようになります。
*2
disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
の設定に必要ですが、 DefaultTyping.EVERYTHING
で使用するには GenericJackson2JsonRedisSerializer
内の一部処理を変更する必要があります。
spring-data-redis
が ver.2.7
以降であれば処理が変更されているようなので、アップデートすれば設定しても問題ないと思われます。
最後に
List.of
は、非常に便利な機能です。
使えるのであればこれを使わない手は無いので、ぜひ今回の設定をしていただき、ガンガン List.of
を使っていきましょう!
なお、spring-data-redis
が ver.2.7
以降であればデフォルトでDefaultTyping.EVERYTHING
となっているようなので、将来的にはこの設定は不要になるかもしれません。
現在のデフォルト設定も見てみると良いでしょう。