Redis 在Springboot 中反序列化 LocalDateTime 时报错

最近使用了 JDK8 中新的时间 API LocalDateTime,中间使用了Redis作为缓存,发现 Springboot 默认使用的 Jackson 无法正确序列化 LocalDateTime,究其原因是 Jackson 在序列化 LocalDateTime 时输出的不是普通的字符串时间格式,而是如下所示的格式

"createTime":{
    "date":			{"year":2020,"month":"FEBRUARY","day":4,"dayOfWeek":"TUESDAY","era":"CE","dayOfYear":35,"leapYear":true,"monthValue":2,"dayOfMonth":4,
                     "chronology":{"id":"ISO","calendarType":"iso8601"},"prolepticMonth":24241
           }

而普通时间格式是:2019-02-27 12:10:17。

以下是抛出的异常:

2020-11-02 22:08:15.606 ERROR 2992 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with 
path [] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot
 construct instance of `java.time.LocalDateTime` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based
 Creator)
 at [Source: (byte[])"["java.util.ArrayList",[["cn.duniqb.mall.tiny.modules.ums.model.UmsResource",{"id":1,"createTime":{"date":
{"year":2020,"month":"FEBRUARY","day":4,"dayOfWeek":"TUESDAY","era":"CE","dayOfYear":35,"leapYear":true,"monthValue":2,"dayOfMonth":4,"chronology":
{"id":"ISO","calendarType":"iso8601"},"prolepticMonth":24241},"time":
{"hour":17,"minute":4,"second":55,"nano":0},"month":"FEBRUARY","dayOfWeek":"TUESDAY","dayOfYear":35,"nano":0,"year":2020,"monthValue":2,"dayOfMonth":4,"hour":17,"minu
te":4,"secon"[truncated 17976 bytes]; line: 1, column: 100] (through reference chain: java.util.ArrayList[0]-
>cn.duniqb.mall.tiny.modules.ums.model.UmsResource["createTime"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct
 instance of `java.time.LocalDateTime` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

所以是原因是 Jackson 序列化 LocalDateTime 跟我们所预想的不一致,将注册给 Redis 的序列化模板修改成以下就行。

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisSerializer<Object> serializer = redisSerializer();
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        redisTemplate.afterPropertiesSet();

        // 下面代码解决LocalDateTime序列化与反序列化不一致问题
        Jackson2JsonRedisSerializer<Object> j2jrs = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 解决jackson2无法反序列化LocalDateTime的问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        j2jrs.setObjectMapper(objectMapper);
        // 序列化 value 时使用此序列化方法
        redisTemplate.setValueSerializer(j2jrs);
        redisTemplate.setHashValueSerializer(j2jrs);

        return redisTemplate;
    }

这样使用就不会出错了。

posted @ 2020-11-02 22:49  duniqb  阅读(2093)  评论(0编辑  收藏  举报