Spring Boot 2.5.0 和 InvalidDefinitionException:默认不支持 Java 8 日期/时间类型`java.time.Instant`

Posted

技术标签:

【中文标题】Spring Boot 2.5.0 和 InvalidDefinitionException:默认不支持 Java 8 日期/时间类型`java.time.Instant`【英文标题】:Spring Boot 2.5.0 and InvalidDefinitionException: Java 8 date/time type `java.time.Instant` not supported by default 【发布时间】:2021-08-24 17:00:09 【问题描述】:

从 Spring Boot 2.4.5 更新到 Spring 2.5.0 后,我注意到应用程序日志中有以下异常:

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.Instant` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: org.telegram.telegrambots.meta.api.objects.Update["my_chat_member"]->org.telegram.telegrambots.meta.api.objects.ChatMemberUpdated["new_chat_member"]->org.telegram.telegrambots.meta.api.objects.ChatMember["untilDateAsInstant"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1215) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1059) ~[jackson-databind-2.12.3.jar!/:2.12.3]
    at org.springframework.jms.support.converter.MappingJackson2MessageConverter.mapToTextMessage(MappingJackson2MessageConverter.java:279) ~[spring-jms-5.3.7.jar!/:5.3.7]
    at org.springframework.jms.support.converter.MappingJackson2MessageConverter.toMessage(MappingJackson2MessageConverter.java:184) ~[spring-jms-5.3.7.jar!/:5.3.7]
    ... 37 common frames omitted

这是我的 pom.xml:

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

我恢复到 Spring Boot 2.4.5,但并非一切正常。 Spring Boot 2.5.0 可能有什么问题?

更新

对应的GitHubhttps://github.com/spring-projects/spring-boot/issues/26859

【问题讨论】:

仅仅添加依赖是不够的。您需要配置对象映射器以添加 Jackson 模块 但是为什么它在 SB 2.4.5 中工作而在 2.5.0 中停止工作? 可能是this。如果是这样,这是一个错误 我个人会等到下一个版本。除了这个问题,2.5.0 也破坏了我们的应用,原因是:github.com/spring-projects/spring-boot/issues/26627 对我来说 - /actuator/info 端点在升级到 sb 2.5.1 后停止工作,并且失败并出现与 Instant 类型相同的 InvalidDefinitionException 和 jsr310 错误。 @Michael 指出的提交是否解决了这个问题? 【参考方案1】:

我在我的一个测试课程中发现了一个问题。问题在于它正在创建一个未添加 JavaTimeModule 的新 ObjectMapper 实例。

这是一个示例测试,它在 Spring 2.4.5 中有效,但在 2.5.0/2.5.1 中失败,出现 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.ZonedDateTime not supported默认

可能是jackson-datatype-jsr310版本升级的原因

package net.jpmchase.gti.gtfabric;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

import java.time.ZonedDateTime;

public class ObjectMapperTest 

    @Test
    public void objectMapperTest() throws Exception

        ZonedDateTime time = ZonedDateTime.now();
        ObjectMapper o = new ObjectMapper();
        o.writeValueAsString(time);
    

要修复这个特定的测试用例必须添加一个显式的

ObjectMapper o = new ObjectMapper();
o.registerModule(new JavaTimeModule()); 

【讨论】:

尝试使用Jackson2ObjectMapperBuilder 类来构建ObjectMapper 类的实例:Jackson2ObjectMapperBuilder.json().build()。在这种情况下,您将获得正确配置的映射器。无论你在哪里有 Spring Context,你也可以注入一个 ObjectMapper 的 bean,Spring Boot 通过 JacksonAutoConfiguration 公开它。 我在单元测试中评估值时遇到了这个问题,这解决了它new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(value);:D 谢谢 @TuGordoBello 同意并建议使用 fluent-mutator 在一行中进行初始化:new ObjectMapper().registerModule(new JavaTimeModule()) 这不是一个答案,只是告诉您如何重现该问题。如果您的依赖项使用 ObjectMapper,则无济于事。没有什么能阻止 spring 捡起损坏的 ObjectMapper。【参考方案2】:

也许你的代码中没有使用spring提供的Object Mapper。

错误的方式:

ObjectMapper o = new ObjectMapper();

正确方法:

@Autowired
Jackson2ObjectMapperBuilder mapperBuilder;

...

ObjectMapper mapper = mapperBuilder.build();

【讨论】:

你能解释一下,为什么构造函数的使用被认为是错误的并且注入的生成器是正确的吗? 如果您自己创建实例,您需要自己配置所有内容,例如添加您需要的所有扩展。使用 Spring 提供的构建器,您已经完成了所有设置。 会为您的答案添加这个很好的解释?️ Spring 的自动配置已经将可用模块(如JavaTimeModule)注册到其默认的ObjectMapper bean。您可以通过向测试类添加一个字段来简单地使用它:@Autowired ObjectMapper mapper;(请参阅related answer)。如果 Spring 的自动配置 ObjectMapper 不够用,我只会使用 builder,我需要 customize a separate one。 没有帮助,因为您无法更改依赖代码以使用 Jackson2ObjectMapperBuilder。【参考方案3】:

如果您使用的是 Spring Data Couchbase,那么这可能是您的问题: https://github.com/spring-projects/spring-data-couchbase/blame/4.2.x/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java#L309

错误报告在这里: https://github.com/spring-projects/spring-data-couchbase/issues/1209

这已在 Spring-Data-Couchbase 4.3 中修复

【讨论】:

以上是关于Spring Boot 2.5.0 和 InvalidDefinitionException:默认不支持 Java 8 日期/时间类型`java.time.Instant`的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 2.5.0 重新设计的spring.sql.init 配置有啥用?

Spring Boot + Oauth 2单点登录ridirect uri错误行为

Spring Boot 2.5.0 重新设计的spring.sql.init 配置有啥用?

Spring Boot 2.5.0 和 InvalidDefinitionException:默认不支持 Java 8 日期/时间类型`java.time.Instant`

Spring Boot 2.5.0 重新设计的spring.sql.init 配置有啥用?

Spring Boot 2.5.0正式版发布,MateCloud微服务平台同步升级