最近新开了一个测试项目,使用了Java8的LocalDateTime替换了之前使用的Date类。接口返回结果时,抛出了序列化异常:
java.lang.reflect.UndeclaredThrowableException: null ………… Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: cn.xx.dao.common.Back["data"]->cn.xx.dao.data.vo.BmVo["spe"]-> cn.xx.dao.entity.Spe["createTime"])
spring boot:2.5.1
jackson-databind:2.12.3
………… org.springframework.boot spring-boot-starter-parent 2.5.1 com.fasterxml.jackson.core jackson-databind 2.12.3 compile
com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.12.3
@JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime createTime;
这时序列化正常,接口返回了结果,但是很明显不是我们需要的结果:
{ "createTime": [ 2023, 3, 20, 17, 34, 37 ] }
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime createTime;
InvalidDefinitionException这个报错,大部分博客提供的思路都是这个,理论上没什么问题,但是实际情况可能需要全局处理如LocalDateTime的时间格式,如果为相关的时间字段一个个的加注解,又太过麻烦。所以这种方法只能用于需要处理特定时间格式的字段上。
鉴于方法A只能作用于局部,而使用Jackson2ObjectMapperBuilderCustomizer时,仍然无法解决InvalidDefinitionException这个异常问题,只能将Spring Boot回退至2.4.x版本,问题得到解决。
参考:Spring Boot 2.5.0 and InvalidDefinitionException: Java 8 date/time type java.time.Instant not supported by default
解决流程:
@Configuration public class GlobalDateConfig { // 序列化时设置的时间格式 private final String formatter = "yyyy-MM-dd HH:mm:ss"; @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> { DateTimeFormatter localDateTimeFormatter = DateTimeFormatter.ofPattern(formatter); //返回时间数据序列化 builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(localDateTimeFormatter)); // 接收时间数据反序列化 builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(localDateTimeFormatter)); }; } }
………… org.springframework.boot spring-boot-starter-parent 2.4.2
Spring Boot 2.4.x使用的是jackson 2.11.x
Spring Boot 2.5.x使用的是jackson 2.12.x
参考:Spring Boot 2.5.0 and InvalidDefinitionException: Java 8 date/time type java.time.Instant not supported by default
com.fasterxml.jackson.core jackson-databind 2.11.2 com.fasterxml.jackson.core jackson-core 2.11.2 com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.11.2
根据Spring Boot的讨论和Stack overflow的一些解决方法做了一些尝试,但是均未成功解决问题。
我的项目中,并不存在这种自定义ObjectMapper且将其设置为全局Bean的情况。
如果存在自定义ObjectMapper Bean的情况下,需要注册处理java8时间序列化的类:JavaTimeModule(或者Jdk8Module)
@Bean @Primary public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); return objectMapper; }
或者:
@Bean @Primary public ObjectMapper objectMapper() { return JsonMapper.builder() .addModule(new JavaTimeModule()) .build(); }
或者
@Resource private Jackson2ObjectMapperBuilder mapperBuilder; @Bean @Primary public ObjectMapper objectMapper() { ObjectMapper build = mapperBuilder.build(); objectMapper.registerModule(new JavaTimeModule()); return objectMapper; }
这几种情况没遇到过,不知道是否可行。总之这里对我的代码不起作用。
此外,JavaTimeModule还支持自定义格式
DateTimeFormatter localDateTimeFormatter = DateTimeFormatter.ofPattern(formatter); // 添加Java8时间序列化反序列化 JavaTimeModule javaTimeModule = new JavaTimeModule(); //返回时间数据序列化 javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(localDateTimeFormatter)); //接收时间数据反序列化 javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(localDateTimeFormatter));
很明显,这个就是实现一个Jackson2ObjectMapperBuilderCustomizer类,与上文中的方法jackson2ObjectMapperBuilderCustomizer作用相同。这里也无法解决开头的问题。
@Configuration public class GlobalDateConfig implements Jackson2ObjectMapperBuilderCustomizer { private final String formatter = "yyyy-MM-dd HH:mm:ss"; @Override public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { jacksonObjectMapperBuilder.simpleDateFormat(formatter) .modules(new JavaTimeModule()) .serializationInclusion(JsonInclude.Include.ALWAYS) .failOnEmptyBeans(false) .failOnUnknownProperties(false) .featuresToEnable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) .featuresToEnable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS); } }
Spring Boot 2.5.0 and InvalidDefinitionException: Java 8 date/time type java.time.Instant not supported by default