最近使用了 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;
}
这样使用就不会出错了。