解决报错InvalidDefinitionException Java 8 datetime type LocalDateTime not supported by default jsr310
作者:mmseoamin日期:2023-12-25

目录

    • 一. 报错信息
    • 二. 版本信息
    • 三. 解决方法
        • 1. 使用@JsonSerialize + @JsonDeserialize注解
        • 2. 回退Spring Boot版本
        • 3. 回退jackson版本
        • 四. 一些尝试(未解决问题)
            • 1. 向自定义ObjectMapper Bean中注册JavaTimeModule
            • 2. 设置Jackson2ObjectMapperBuilder类
            • 参考

              一. 报错信息

              最近新开了一个测试项目,使用了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
                    
              
              





              三. 解决方法

              1. 使用@JsonSerialize + @JsonDeserialize注解
              1. pom加上这两个注解的依赖
              
                  com.fasterxml.jackson.datatype
                  jackson-datatype-jsr310
                  2.12.3
              
              
              1. 在使用LocalDateTime的属性上添加这两个序列化反序列化注解:
              @JsonSerialize(using = LocalDateTimeSerializer.class)
              @JsonDeserialize(using = LocalDateTimeDeserializer.class)
              private LocalDateTime createTime;
              

              这时序列化正常,接口返回了结果,但是很明显不是我们需要的结果:

              {
              	"createTime": [
              		2023,
              		3,
              		20,
              		17,
              		34,
              		37
              	]
              }
              
              1. 加上@JsonFormat来格式化时间,现在就可以正常输出了:
              @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
              @JsonSerialize(using = LocalDateTimeSerializer.class)
              @JsonDeserialize(using = LocalDateTimeDeserializer.class)
              private LocalDateTime createTime;
              

              InvalidDefinitionException这个报错,大部分博客提供的思路都是这个,理论上没什么问题,但是实际情况可能需要全局处理如LocalDateTime的时间格式,如果为相关的时间字段一个个的加注解,又太过麻烦。所以这种方法只能用于需要处理特定时间格式的字段上。



              2. 回退Spring Boot版本

              鉴于方法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

              解决流程:

              1. 设置Jackson2ObjectMapperBuilderCustomizer:
              @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));
                      };
                  }
              }
              
              1. 回退Spring Boot版本至2.4.x
              ……
              
                  org.springframework.boot
                  spring-boot-starter-parent
                  2.4.2
                   
              
              ……
              
              1. 重启执行,问题解决



              3. 回退jackson版本

              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

              1. 无须修改Spring Boot版本,并显式的指定jackson版本(2.13也报这个错误,原因可以参考这里:a change in Jackson 2.12、comment of 2.4 and 2.5)
              
                  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
              
              
              1. 使用上文的GlobalDateConfig配置,重启执行,问题解决





              四. 一些尝试(未解决问题)

              根据Spring Boot的讨论和Stack overflow的一些解决方法做了一些尝试,但是均未成功解决问题。

              1. 向自定义ObjectMapper Bean中注册JavaTimeModule

              我的项目中,并不存在这种自定义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));
              



              2. 设置Jackson2ObjectMapperBuilder类
              1. 设置Jackson2ObjectMapperBuilder类,控制序列化和反序列化操作。

              很明显,这个就是实现一个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