PAYATHON 2026

Spring Boot 4 如何自定义 ObjectMapper 并保留现有配置?

数据小陈

Spring Boot 4 默认使用 Jackson 3。要自定义 mapper 配置,应声明 JsonMapperBuilderCustomizer bean,由 Spring Boot 在创建 JsonMapper 时应用这些设置。这样可以保留自动配置流程,以及配置文件中未被显式覆盖的设置。官方接口说明

Jackson 3 的 mapper 配置不可变,应在构建阶段完成定制,不能再通过 setTimeZone(...) 修改已创建的实例。Spring 官方迁移说明

为什么需要调整原来的写法?

问题中的代码采用 Jackson 2 的配置方式:先用 Jackson2ObjectMapperBuilder 构建 ObjectMapper,再设置时区。Spring Boot 4 默认切换到了 Jackson 3,并为 JSON 自动配置 tools.jackson.databind.json.JsonMapper。它继承自 Jackson 3 的 ObjectMapper。Spring Boot 4 JSON 文档

需要区分两个问题:

  • 在 Spring Boot 3 中,通过自动配置的 builder 创建 mapper 时,仍可能继承已应用到 builder 的配置,因此不能断言原代码一定丢失了全部设置。
  • 自行提供 mapper bean 会接管相应实例的创建。如果只想增加配置,使用 customizer 更合适。@Primary 只影响注入时的优先选择,不负责合并配置。

Jackson 2 和 Jackson 3 的 ObjectMapper 也属于不同的包。保留一个 com.fasterxml.jackson.databind.ObjectMapper bean,不等于定制了默认的 Jackson 3 JsonMapper。Spring 官方说明

Jackson 3:使用 JsonMapperBuilderCustomizer

移除原来用于替代默认 mapper 的 @Bean 方法,改为注册下面的配置类,并确保它位于组件扫描范围内:

import java.util.TimeZone;

import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

import tools.jackson.databind.MapperFeature;

@Configuration(proxyBeanMethods = false)
public class JacksonConfig {

    @Bean
    @Order(1)
    public JsonMapperBuilderCustomizer jsonMapperCustomizer() {
        return builder -> builder
                .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
                .defaultTimeZone(TimeZone.getDefault());
    }
}

这个 bean 返回 customizer,mapper 仍由 Spring Boot 创建。无需调用 build(),也无需声明 @Primary。

原代码显式采用 JVM 默认时区,defaultTimeZone(TimeZone.getDefault()) 对应这一意图。Jackson 3 的 builder 提供了这个方法。Jackson 3 builder 源码

Spring Boot 自带的 customizer 顺序为 0。示例使用 @Order(1),让这段定制在它之后执行。如果配置文件也设置了相同属性,代码中的设置会覆盖它;其他 customizer 仍可能继续覆盖同一项。Spring Boot 定制说明

如果希望配置文件决定这些设置

“保留配置”不代表同一个选项能同时采用两个不同值。上面的代码明确指定了时区,因此不会保留配置文件中不同的时区值。

如果这些选项需要按环境调整,可以直接放进 application.yml,不必为它们编写 customizer:

spring:
  jackson:
    mapper:
      accept-case-insensitive-properties: true
    time-zone: Asia/Shanghai

Asia/Shanghai 是配置示例,应替换为实际需要的时区;它不等同于动态读取 TimeZone.getDefault()。spring.jackson.mapper.* 用于配置 MapperFeature,spring.jackson.time-zone 用于指定日期格式化的时区。特性配置说明、配置属性参考

如果还需要 customizer 处理其他逻辑,可以只在其中设置需要由代码控制的选项。例如,要保留配置文件中的时区,删除示例中的 .defaultTimeZone(...) 即可。

如果项目仍在使用 Jackson 2

Spring Boot 4 也提供用于过渡的 spring-boot-jackson2 模块。如果项目实际使用这套自动配置,应使用 Jackson 2 对应的 customizer,配置属性前缀为 spring.jackson2.*。这套支持已弃用,适用于迁移阶段。Jackson 2 兼容说明

对应的配置类可以写成:

import java.util.TimeZone;

import com.fasterxml.jackson.databind.MapperFeature;

import org.springframework.boot.jackson2.autoconfigure.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

@Configuration(proxyBeanMethods = false)
public class Jackson2Config {

    @Bean
    @Order(1)
    public Jackson2ObjectMapperBuilderCustomizer jackson2Customizer() {
        return builder -> builder
                .featuresToEnable(
                        MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
                .timeZone(TimeZone.getDefault());
    }
}

这里使用的是 Spring Boot 4 中的包名:org.springframework.boot.jackson2.autoconfigure.Jackson2ObjectMapperBuilderCustomizer。接口文档

如果 Jackson 2 和 Jackson 3 同时存在,需要确认实际执行 JSON 转换的 mapper 属于哪一套。两种 customizer 分别作用于各自的自动配置,不能互相替代。

如何确认配置生效?

验证时使用 Spring 注入的 mapper,避免在验证代码中自行 new ObjectMapper() 或调用独立的 JsonMapper.builder().build()。

可以检查以下行为:

  • 用大小写不同的 JSON 字段反序列化到已有 Java 属性,确认大小写不敏感设置生效。
  • 选择配置文件中另一项已有设置,确认添加 customizer 后仍然有效。
  • 如果目标是 HTTP 接口,同时验证实际接口的 JSON 输入输出,确认它使用了预期的 mapper。

时区应结合实际日期类型和序列化方式验证,不能仅凭设置了默认时区就推断所有日期字段都会产生相同变化。

备注:内容仅供参考。