Spring Boot 4 注入 ObjectMapper 报找不到 Bean:升级 Jackson 3 的改法和过渡方案(4.1.2 实测)

导读 :项目从 Spring Boot 3 升到 4 之后,最常见的一类报错是 程序包com.fasterxml.jackson.databind不存在,或者启动时提示 required a bean of type 'com.fasterxml.jackson.databind.ObjectMapper' that could not be found。原因是 Boot 4 默认换成了 Jackson 3。本文在 Spring Boot 4.1.2 + JDK 17 上把这两种报错都复现了一遍,给出正式改法和临时过渡的办法,顺带列出升级后 JSON 输出会悄悄变化的地方。


一、先说清楚:这个报错是怎么回事

Jackson 是 Spring Boot 里负责 Java 对象和 JSON 互转的库,Controller 返回对象变成 JSON、@RequestBody 把 JSON 变成对象,背后都是它。平时注入的 ObjectMapper,就是 Spring Boot 自动创建好、按 spring.jackson.* 配置过的那个实例。

Jackson 出了 3.0 大版本,包名整体从 com.fasterxml.jackson 改成了 tools.jackson (Maven groupId 也从 com.fasterxml.jackson.core 换成 tools.jackson.core)。Spring Boot 4 默认用的就是 Jackson 3,自动配置只创建 Jackson 3 的 JsonMapper,不再创建老包名下的 ObjectMapper。

所以老代码里 import com.fasterxml.jackson.databind.ObjectMapper 的地方会出两种情况:

你的依赖 现象
只引了 Spring Boot 的 starter,没单独引 jackson-databind 编译失败:程序包com.fasterxml.jackson.databind不存在
pom 里单独引过 com.fasterxml.jackson.core:jackson-databind(或者某个三方包带进来了) 能编译,启动失败:找不到 com.fasterxml.jackson.databind.ObjectMapper 类型的 Bean

不处理的话应用起不来,没有"带病运行"这一说。什么时候需要动它:只要你准备升 Spring Boot 4,并且代码里有任何地方注入或 import 了 Jackson 2 的类。下面先复现,再说改法。


二、实测环境

项 版本
Spring Boot 4.1.2(Maven Central 上当前最新 GA,4.2.0 还在 RC1)
Jackson 3.1.7(Boot 4.1.2 管理的版本)
JDK 17.0.12
Maven 3.6.3
依赖 只有 spring-boot-starter-webmvc

先看依赖树里 Jackson 是哪个版本:

bash 复制代码
# 在项目根目录执行,只看 jackson 相关的行
mvn dependency:tree | grep -i jackson

4.1.2 的输出:

text 复制代码
+- org.springframework.boot:spring-boot-starter-jackson:jar:4.1.2:compile
|  \- org.springframework.boot:spring-boot-jackson:jar:4.1.2:compile
|     \- tools.jackson.core:jackson-databind:jar:3.1.7:compile
|        +- com.fasterxml.jackson.core:jackson-annotations:jar:2.21:compile
|        \- tools.jackson.core:jackson-core:jar:3.1.7:compile

注意最后两行:jackson-annotations 还在老的 com.fasterxml.jackson.core 下面。这是 Jackson 3 唯一保留老包名的模块,所以 @JsonProperty、@JsonIgnore、@JsonFormat 这些注解的 import 不用改 。要改的是 ObjectMapper、JsonNode、JsonProcessingException 这类 databind 和 core 里的类。


三、复现两种报错

3.1 编译失败:程序包不存在

一个最普通的 Service,构造器注入 ObjectMapper:

java 复制代码
package demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;

// Boot 3 时代的写法,原样搬到 Boot 4
@Service
class OrderService {
    private final ObjectMapper objectMapper;

    OrderService(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
}

mvn compile 直接失败:

text 复制代码
[ERROR] App.java:[3,38] 程序包com.fasterxml.jackson.databind不存在
[ERROR] App.java:[17,19] 找不到符号
  符号:   类 ObjectMapper
  位置: 类 demo.OrderService

3.2 启动失败:找不到 Bean

很多人看到编译错误的第一反应是在 pom 里补一个 jackson-databind:

xml 复制代码
<!-- 不带版本号,Boot 4 仍然管理 Jackson 2 的版本,实测解析到 2.21.7 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

这样编译能过,但启动报错:

text 复制代码
***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in demo.OrderService required a bean of type 'com.fasterxml.jackson.databind.ObjectMapper' that could not be found.

Action:

Consider defining a bean of type 'com.fasterxml.jackson.databind.ObjectMapper' in your configuration.

这就是标题里那个报错。类在 classpath 上,但 Spring Boot 4 的自动配置只认 Jackson 3,没人去创建这个 Bean。

⚠️ 老项目里这个依赖经常是三方 SDK 传递进来的,你自己的 pom 里没写也会走到 3.2 这种情况。先用 mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind 看看是谁带进来的。


四、正式改法:换成 Jackson 3 的 JsonMapper

4.1 改 import 和注入类型

把注入的类型换成 tools.jackson.databind.json.JsonMapper。Boot 4 自动配置的就是这个类型:

java 复制代码
package demo;

import org.springframework.stereotype.Service;
import tools.jackson.databind.json.JsonMapper;

@Service
class OrderService {
    private final JsonMapper jsonMapper;

    OrderService(JsonMapper jsonMapper) {
        this.jsonMapper = jsonMapper;
    }

    // Jackson 3 的 writeValueAsString 不再抛受检异常,方法签名不用 throws
    String toJson(Order order) {
        return jsonMapper.writeValueAsString(order);
    }
}

注入 tools.jackson.databind.ObjectMapper 也可以。我在启动后打印了两个注入点,是同一个实例:

text 复制代码
same bean: true, class=tools.jackson.databind.json.JsonMapper

我的建议是统一注入 JsonMapper。官方迁移指南里写了:Boot 4 开始,想替换 自动配置的实例,只声明一个 ObjectMapper Bean 已经不够了,要声明 JsonMapper Bean。业务代码也用 JsonMapper,和配置那边是一个类型,排查时少绕一层。

常用类的对照:

Jackson 2(Boot 3) Jackson 3(Boot 4)
com.fasterxml.jackson.databind.ObjectMapper tools.jackson.databind.json.JsonMapper(或 tools.jackson.databind.ObjectMapper)
com.fasterxml.jackson.databind.JsonNode tools.jackson.databind.JsonNode
com.fasterxml.jackson.core.JsonProcessingException(受检异常) tools.jackson.core.JacksonException(运行时异常)
com.fasterxml.jackson.databind.SerializationFeature tools.jackson.databind.SerializationFeature
com.fasterxml.jackson.annotation.JsonProperty 等注解 不变,还是 com.fasterxml.jackson.annotation

大项目里先用 grep 圈出要改的文件:

bash 复制代码
# 列出 import 了 Jackson 2 databind / core 的 Java 文件,注解包不在其中
grep -rlE "import com\.fasterxml\.jackson\.(databind|core)\." src/main/java src/test/java

4.2 异常处理会顺带变简单

Jackson 2 里 readValue、writeValueAsString 都抛 JsonProcessingException,是受检异常,到处都是 try-catch 或 throws。Jackson 3 改成了运行时异常 JacksonException。实测解析一段坏 JSON:

java 复制代码
try {
    objectMapper.readValue("{bad json", Map.class);
} catch (JacksonException e) {
    // 打印异常类名,确认它是不是 RuntimeException
    System.out.println("caught " + e.getClass().getName() + " runtime=" + (e instanceof RuntimeException));
}

输出:

text 复制代码
caught tools.jackson.core.exc.StreamReadException runtime=true

所以原来那些只为了满足编译器而写的 catch (JsonProcessingException e) { throw new RuntimeException(e); } 可以删掉。全局异常处理器里如果按 JsonProcessingException 做了分支,要改成 JacksonException,否则那个分支永远不会命中。

4.3 Customizer 改名

Boot 3 里常用 Jackson2ObjectMapperBuilderCustomizer 调全局配置。Boot 4 里原来的包 org.springframework.boot.autoconfigure.jackson 已经没了,实测老 import 编译报:

text 复制代码
[ERROR] App.java:[7,54] 程序包org.springframework.boot.autoconfigure.jackson不存在

换成 JsonMapperBuilderCustomizer,包名也变了:

java 复制代码
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import tools.jackson.databind.SerializationFeature;

// 写在任意 @Configuration 类里,作用和 Boot 3 的 Jackson2ObjectMapperBuilderCustomizer 一样
@Bean
JsonMapperBuilderCustomizer indentCustomizer() {
    return builder -> builder.enable(SerializationFeature.INDENT_OUTPUT);
}

注入的 JsonMapper 输出确实带了缩进,说明 Customizer 生效了。

官方迁移指南里还列了几个同类改名,有用到的一起改:

Boot 3 Boot 4
Jackson2ObjectMapperBuilderCustomizer JsonMapperBuilderCustomizer
@JsonComponent @JacksonComponent
@JsonMixin @JacksonMixin
JsonObjectSerializer / JsonValueDeserializer ObjectValueSerializer / ObjectValueDeserializer
spring.jackson.read.* / spring.jackson.write.* spring.jackson.json.read.* / spring.jackson.json.write.*

💡 配置项改名这类问题,可以配合 spring-boot-properties-migrator 一起查,启动日志会列出改了名的 key。


五、升级后 JSON 输出会悄悄变的地方

改完能启动了,还要看接口返回的 JSON 有没有变化。前端或下游按字符串解析日期的,最容易在这里出问题。

我用同一个对象分别在默认配置和 spring.jackson.use-jackson2-defaults=true 下序列化:

java 复制代码
// 测试用的对象:一个 LocalDateTime,一个老式的 java.util.Date
record Order(Long id, LocalDateTime createTime, Date payTime) {}

Boot 4.1.2 默认:

json 复制代码
{
  "id" : 1001,
  "createTime" : "2026-10-30T09:00:00",
  "payTime" : "1970-01-01T00:00:00.000Z"
}

加上 --spring.jackson.use-jackson2-defaults=true:

json 复制代码
{
  "id" : 1001,
  "createTime" : "2026-10-30T09:00:00",
  "payTime" : "1970-01-01T00:00:00.000+00:00"
}

LocalDateTime 两种情况一样,java.util.Date 的时区后缀从 +00:00 变成了 Z。两种写法在 ISO 8601 里含义相同,标准的日期库都能解析;但如果下游是用正则或者字符串截取处理的,就会出错。

spring.jackson.use-jackson2-defaults 是 Boot 4 专门加的兼容开关,打开后自动配置的 JsonMapper 会尽量对齐 Boot 3 时代 Jackson 2 的默认行为。我的建议:

  • 有对外接口、下游不归你管的服务,升级时先打开这个开关,保证返回格式不变,后面再单独评估要不要关
  • 内部服务、前后端都在自己手里的,直接用 Jackson 3 默认值,把日期字段的契约测试补上

⚠️ 上面只对比了 LocalDateTime 和 Date 两种类型。项目里如果有自定义序列化器、Optional、枚举、空值处理的特殊配置,升级后要用自己的接口回归一遍,不要只靠这一个对比。


六、一时改不完:spring-boot-jackson2 过渡方案

老项目里 Jackson 2 的代码到处都是,或者某个三方 SDK 只认 Jackson 2 的 ObjectMapper,一次改不完。Boot 4 提供了一个过渡模块:

xml 复制代码
<!-- Boot 4 自带的 Jackson 2 兼容模块,版本由 parent 管理 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-jackson2</artifactId>
</dependency>

加上之后,3.2 节那段注入老 ObjectMapper 的代码能正常启动了:

text 复制代码
jackson2 bean: com.fasterxml.jackson.databind.ObjectMapper

几个要注意的地方,都是这次实测看到的:

点 实测情况
模块状态 类上标了 @Deprecated(forRemoval=true),编译时提示"已过时, 且标记为待删除";官方说法是给需要更多时间的用户临时用,以后会删
Customizer 包名 还叫 Jackson2ObjectMapperBuilderCustomizer,但包名变成 org.springframework.boot.jackson2.autoconfigure,老 import 照样要改
配置前缀 这个 Jackson 2 Bean 读的是 spring.jackson2.*(模块的配置元数据里能看到),不是 spring.jackson.*
HTTP 消息转换 spring.http.converters.preferred-json-mapper 默认值是 jackson,也就是 Controller 的 JSON 仍走 Jackson 3;元数据里写着可以设成 jackson2(已标 deprecated)

最后一行很容易踩坑:加了过渡模块,你注入的 ObjectMapper 是 Jackson 2 的,但 Controller 返回 JSON 用的还是 Jackson 3。两边配置分别在 spring.jackson2.* 和 spring.jackson.* 下,日期格式之类的配置只写了一边,就会出现"手动序列化和接口返回格式不一样"。preferred-json-mapper=jackson2 这条我只看了配置元数据,没有起 Web 服务实测切换效果。

我的建议是把过渡模块当成"先让应用启动起来"的手段,排期把代码迁到 Jackson 3,而不是长期留着。


七、速查表

现象 原因 处理
程序包com.fasterxml.jackson.databind不存在 Boot 4 只带 Jackson 3 import 改成 tools.jackson.*,注入 JsonMapper
启动报 required a bean of type 'com.fasterxml.jackson.databind.ObjectMapper' classpath 有 Jackson 2,但没人创建 Bean 同上;改不完先加 spring-boot-jackson2
程序包org.springframework.boot.autoconfigure.jackson不存在 Customizer 换了名字和包 改用 org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
catch (JsonProcessingException e) 报错或不再命中 Jackson 3 改成运行时异常 改成 JacksonException,删掉多余的 try-catch
接口里 Date 字段的时区后缀从 +00:00 变成 Z Jackson 3 默认值变化 对外接口先开 spring.jackson.use-jackson2-defaults=true
@JsonProperty 等注解要不要改 不用改 jackson-annotations 仍在 com.fasterxml.jackson.annotation

八、常见问题

Q:我只是 Controller 返回对象,没手动注入 ObjectMapper,还用管吗?

代码上不用改,但第五节的输出变化照样会影响你,接口返回格式要回归一遍。

Q:能不能自己 new 一个 Jackson 2 的 ObjectMapper 注册成 Bean,把报错糊过去?

能启动,但这个实例不会读 spring.jackson.* 配置,日期格式、时区、命名策略都跟原来不一样,相当于换了一套没配置过的 Jackson。要临时兼容就用 spring-boot-jackson2,至少它会读 spring.jackson2.*。

Q:三方 SDK 方法参数要求传 Jackson 2 的 ObjectMapper 怎么办?

先看 SDK 有没有出支持 Jackson 3 的新版本。没有的话,用 spring-boot-jackson2 过渡,或者只在调用 SDK 的那个类里单独构建一个 Jackson 2 实例,别让它扩散到业务代码。

Q:4.2 还会有变化吗?

4.2.0 目前还是 RC1,本文只在 4.1.2 GA 上实测。4.2 的 Maven Central 上 spring-boot-jackson2 仍有 RC1 版本,说明过渡模块在 4.2 还没删。


参考链接


你们项目升 Boot 4 时,Jackson 这块是一次性迁到 Jackson 3,还是先挂了 spring-boot-jackson2 过渡?有没有碰到日期格式变化被下游发现的情况?评论区说说你的版本和做法,典型的我补进 FAQ。

觉得有用的话点个赞、收个藏,升级时翻得到。

相关推荐
猿究院--Cu-Sn合金3 小时前
Spring Boot + DeepSeek 求职 Agent 实践:工具注册、多轮调用与简历 JD 对比
数据库·人工智能·spring boot·后端·deepseek
MetaLite4 小时前
Spring Boot 加了 @Valid,嵌套参数为什么还没校验?
java·spring boot·后端
霸道流氓气质4 小时前
Lingua 语言检测库:n-gram 统计模型、置信度阈值调优与 Spring Boot 分层检测管道的生产级实战
java·spring boot·后端
她的男孩5 小时前
低代码加字段后表结构怎么同步:预览安全 DDL,高风险变更再强制执行
java·spring boot·低代码
huaweichenai5 小时前
spring boot对接kafka实现消息队列处理
spring boot·后端·kafka
Thomas.Sir5 小时前
第11课:文件上传下载、跨域CORS配置,SpringBoot4.x跨域新方案
java·spring boot
寰梦5 小时前
Spring Boot + MinIO 大文件上传完整实战:MD5 秒传、分片上传、断点续传与引用一致性
java·spring boot·文件上传·minio
行者-全栈开发5 小时前
【码动四季·秋】把 Spring Boot 的 Tomcat 换成 Undertow:一次容器替换的三处暗坑
spring boot·tomcat·容器化·依赖管理·undertow
长弓三石6 小时前
Spring Boot 4.1 + AgentScope:企业级 Agent 平台的 MCP 双向集成实战
人工智能·spring boot·后端·harness·开源智能体