Spring Boot 4与 Jackson 3在 ADJUST_DATES_TO_CONTEXT_TIME_ZONE上的问题

后端开发 2026-07-09

在Spring Boot 4中似乎进行了较大重构,我想使用OffsetDateTime,但它总是被转换为UTC,这是默认值,但我想禁用默认设置。此外,这似乎只在网络客户端上出现这个问题。下面给出分解,我的Spring HTTP接口正在接收这个JSON Web服务

{
  "data": {
    "id": 1,
    "content": "Hello, test!",
    "createdAt": "2026-04-19T08:30:00-04:00",
    "createdAt2": "2026-04-19T08:30:00-04:00"
  }
}

我的控制器方法

@GetMapping("/data")
public DataRecord getData() {
    return dataClient.getData();
}

Http接口

@HttpExchange(url = "http://localhost:3000")
public interface DataClient {

    @GetExchange("/data")
    DataRecord getData();
}

Record

public record DataRecord(
    @JsonProperty("id") Long id,
    @JsonProperty("content") String content,
    @JsonFormat(without = JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, with = {})
    @JsonProperty("createdAt") OffsetDateTime createdAt,
    @JsonProperty("createdAt2") OffsetDateTime createdAt2
) {}

以及我在application.properties中的设置

# Old way
#spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
# New way
spring.jackson.datatype.datetime.adjust-dates-to-context-time-zone=false
spring.jackson.datatype.datetime.write-dates-with-context-time-zone=false

这些设置应该保留原始偏移量,但它们不起作用。相反,在调用控制器时我得到如下结果

{
  "id": 1,
  "content": "Hello, test!",
  "createdAt": "2026-04-19T08:30:00-04:00",
  "createdAt2": "2026-04-19T12:30:00Z"
}

The createdAt2日期被转换为UTC即使它被设定为不这样做?我发现唯一有效的是在createdAt字段上的这个注解

@JsonFormat(without = JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, with = {})
@JsonProperty("createdAt") OffsetDateTime createdAt

至少这解释了为何createdAt至少是正确的。不过我在找一个全局设置,这样就不需要在每个OffsetDateTime字段上都标注注解。可行吗?

编辑: 我创建了一个简单的GitHub仓库来测试这个问题 https://github.com/peterfk/spring-boot-4-rest-client-offsetdatetime-bug

解决方案

The issue with Spring Boot 4 + Jackson 3 and ADJUST_DATES_TO_CONTEXT_TIME_ZONE

在Spring Boot 4中,用于JavaTime模块配置的 application.properties 方式发生了显著变化。属性:

spring.jackson.datatype.datetime.adjust-dates-to-context-time-zone=false

属性可能无法可靠地应用到HTTP接口客户端,因为它们使用一个单独配置的 HttpServiceProxyFactory,它有自己的 WebClient,根据你的设置,可能无法拾取自动配置的 ObjectMapper

最可靠的解决方案是显式地配置一个全局的 ObjectMapper bean,使用你需要的JavaTimeModule设置,或者一个更简单、干净的等效实现:

@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return JsonMapper.builder()
            .addModule(new JavaTimeModule())
            .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
            .disable(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE)
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .build();
    }
}Then make sure your HttpServiceProxyFactory uses this ObjectMapper explicitly:
java@Configuration
public class WebClientConfig {

    @Bean
    public DataClient dataClient(ObjectMapper objectMapper) {
        // Ensure WebClient uses your configured ObjectMapper
        WebClient webClient = WebClient.builder()
            .codecs(configurer -> configurer
                .defaultCodecs()
                .jackson2JsonDecoder(
                    new Jackson2JsonDecoder(objectMapper)
                )
            )
            .build();

        HttpServiceProxyFactory factory = HttpServiceProxyFactory
            .builderFor(WebClientAdapter.create(webClient))
            .build();

        return factory.createClient(DataClient.class);
    }
}

为什么会这样工作:HTTP接口客户端(@HttpExchange)使用它们自己的编解码流水线。即使你的全局 ObjectMapper 配置正确,下面的 WebClient 仍可能使用默认编解码器,除非你显式地将你的 ObjectMapper 注入其中。这很可能解释了字段上的 @JsonFormat 至少在字段级别是有效的,但全局属性却不起作用。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章