解决Redis反序列化问题
本文介绍解决Redis反序列化报错java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx
报错的方法。
- 序列化和反序列化:
- 当你使用
Jackson2JsonRedisSerializer
将对象存储在 Redis 中时,Jackson 会将对象序列化为 JSON。 - 当你从 Redis 中读取对象时,Jackson 会将 JSON 反序列化为对象。
- 当你使用
- 类型信息:
- 默认情况下,Jackson 在序列化 JSON 时可能不会包含类型信息。这意味着在反序列化时,Jackson 无法知道具体的类是什么,并且会默认使用通用类型(如
LinkedHashMap
)。 - 在 JSON 中包含类型信息可以帮助 Jackson 理解要反序列化成的确切类。
- 默认情况下,Jackson 在序列化 JSON 时可能不会包含类型信息。这意味着在反序列化时,Jackson 无法知道具体的类是什么,并且会默认使用通用类型(如
- activateDefaultTyping 方法:
activateDefaultTyping
方法用于在序列化的 JSON 中包含类型信息。ObjectMapper.DefaultTyping.NON_FINAL
参数告诉 Jackson 包含所有非 final 类的类型信息。JsonTypeInfo.As.PROPERTY
参数指定类型信息应该作为属性包含在 JSON 中。
通过这样配置 ObjectMapper
,你可以确保在将对象序列化为 JSON 并存储在 Redis 中时,包含类型信息。这样,当 Jackson 从 Redis 中读取数据时,可以正确地将 JSON 反序列化回原始的 Java 对象,从而避免类型转换异常。
package com.sky.config;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sky.json.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* Redis配置类,用于设置RedisTemplate的自定义序列化器。
* 此配置确保Java对象(包括Java 8日期/时间类型)在存储到Redis时能够正确序列化和反序列化。
*/
@Configuration
@Slf4j
public class RedisConfiguration {
/**
* 定义一个RedisTemplate Bean,具有自定义的key和value序列化器。
* RedisTemplate配置为使用处理Java 8日期/时间类型的自定义Jackson ObjectMapper。
*
* @param redisConnectionFactory 用于创建Redis连接的工厂
* @return 配置好的RedisTemplate实例
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("redis配置类加载成功"); // 记录Redis配置类加载成功的信息
// 创建一个新的RedisTemplate实例
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置Redis连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用自定义的Jackson ObjectMapper处理Java 8日期/时间类型
ObjectMapper objectMapper = new JacksonObjectMapper();
// 设置序列化时包含类信息,保证反序列化时jackjson可以把数据转换成正确的对象
objectMapper.activateDefaultTyping(
objectMapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
// 配置RedisTemplate的value序列化器,使用自定义的ObjectMapper
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
serializer.setObjectMapper(objectMapper);
// 设置RedisTemplate的key序列化器为StringRedisSerializer,将key序列化为字符串
// 设置RedisTemplate的value序列化器为Jackson2JsonRedisSerializer,将value序列化为JSON字符串
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
// 设置RedisTemplate的hash key序列化器为StringRedisSerializer,将hash key序列化为字符串
// 设置RedisTemplate的hash value序列化器为Jackson2JsonRedisSerializer,将hash value序列化为JSON字符串
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
// 返回配置好的RedisTemplate实例
return redisTemplate;
}
/**
* 配置RedisCacheManager,使用自定义的RedisCacheConfiguration。
* 处理springcache存储到Redis时的序列化问题
* @param redisConnectionFactory 用于创建Redis连接的工厂
* @return 配置好的RedisCacheManager实例
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 使用自定义的Jackson ObjectMapper处理Java 8日期/时间类型
ObjectMapper objectMapper = new JacksonObjectMapper();
// 设置序列化时包含类信息,保证反序列化时jackjson可以把数据转换成正确的对象
objectMapper.activateDefaultTyping(
objectMapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
// 配置缓存序列化器,使用自定义的Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
serializer.setObjectMapper(objectMapper);
// 创建默认的缓存配置
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ZERO) // 设置缓存的默认过期时间,这里设置为1小时,设置为永久不过期:Duration.ZERO
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))//设置缓存键的序列化器。
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));//设置缓存值的序列化器。
// 返回配置好的RedisCacheManager实例
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfig)
.build();
}
}