bigywater.tech
Spring · 150

Jackson 3 与 Spring Boot 4:包名变了,哪些代码编译不过

Spring Boot 4 用的是 Jackson 3,包名和很多 API 都变了。哪些代码编译不过?默认行为有什么不同?spring.jackson.* 的配置还有效吗?这篇用 Jackson 2.21.5 与 3.1.5 同一份代码逐项实测。

8 / 20
20 段 Jackson 2 写法只换包名之后,在 Jackson 3 上能编译通过的个数(Jackson 2 上是 19 / 20)
启动失败
spring.jackson.serialization.write-dates-as-timestamps=true 在 Boot 4.0.8 上让应用启动失败,要改成 spring.jackson.datatype.datetime.write-dates-as-timestamps
n=1 与 400
Jackson 3 默认忽略未知属性读出 n=1,但 {"n":null} 读进 int 抛 MismatchedInputException,在 Boot 4 里返回 400

包名

实测 · 本机执行输出
  Jackson 2 的 classpath:jackson-annotations-2.21、jackson-core-2.21.5、jackson-databind-2.21.5、jackson-datatype-jdk8-2.21.5、jackson-datatype-jsr310-2.21.5
  Jackson 3 的 classpath:jackson-annotations-2.21、jackson-core-3.1.5、jackson-databind-3.1.5
  jackson-databind-3.1.5.jar 里是否含 java.time 支持(tools.jackson.databind.ext.javatime):True
  jackson-annotations 里的包:com.fasterxml.jackson.annotation

core 与 databind 搬到 tools.jackson,注解包不变,java.time 内置。

编译

实测 · 本机执行输出
  Jackson 2:通过 19 / 20;Jackson 3(只换包名):通过 8 / 20
    new ObjectMapper():2→通过;3→通过
    mapper.configure(feature, boolean):2→通过;3→失败(cannot find symbol)
    mapper.enable(SerializationFeature.…):2→通过;3→失败(cannot find symbol)
    mapper.disable(DeserializationFeature.…):2→通过;3→失败(cannot find symbol)
    catch (JsonProcessingException e):2→通过;3→失败(cannot find symbol)
    catch (IOException e):2→通过;3→失败(exception IOException is never thrown in body of correspondi)
    不写 try/catch 调用 writeValueAsString:2→失败(unreported exception JsonProcessingException; must);3→通过
    registerModule(new JavaTimeModule()):2→通过;3→失败(package tools.jackson.datatype.jsr310 does not exist)
    mapper.setSerializationInclusion(NON_NULL):2→通过;3→失败(cannot find symbol)
    mapper.setPropertyNamingStrategy(…):2→通过;3→失败(cannot find symbol)
    JsonNode.fields():2→通过;3→失败(cannot find symbol)
    JsonNode.isTextual() / asText():2→通过;3→通过
    JsonSerializer 子类:2→通过;3→失败(cannot find symbol)
    JsonDeserializer 子类:2→通过;3→失败(cannot find symbol)
    @JsonProperty 注解(annotation 包):2→通过;3→通过
    mapper.writerWithDefaultPrettyPrinter():2→通过;3→通过
    JsonMapper.builder().build():2→通过;3→通过
    TypeReference(core.type):2→通过;3→通过
    mapper.getFactory().createGenerator(Writer):2→通过;3→失败(cannot find symbol)
    mapper.convertValue / valueToTree:2→通过;3→通过
  再用 Jackson 3 自己的写法验证 8 段:通过 7 段
    JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build():通过
    catch (JacksonException e):通过
    JsonNode.properties():通过
    ValueSerializer 子类:通过
    ValueDeserializer 子类:通过
    builder().propertyNamingStrategy(SNAKE_CASE):通过
    DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS:通过
    SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:失败(cannot find symbol)

只换包名只有 8 / 20 能编译:configure、异常类、JsonNode.fields、序列化器基类等要改写。

默认行为

实测 · 本机执行输出
[Jackson 2.21.5,new ObjectMapper()]
  LocalDate.of(2026,10,23) → 异常 InvalidDefinitionException
  new java.util.Date(0) → 0
  Duration.ofSeconds(90) → 异常 InvalidDefinitionException
  Optional.of(1) / Optional.empty() → 异常 InvalidDefinitionException / 异常 InvalidDefinitionException
  字段声明顺序 b、a、c → {"b":"1","a":"2","c":3}
  LinkedHashMap 插入顺序 z、a、m → {"z":1,"a":2,"m":3}
  空对象 new Empty() → 异常 InvalidDefinitionException
  {"n":1,"zzz":2} 读进只有 n 的类 → 异常 UnrecognizedPropertyException
  {"n":null} 读进 int 字段 → 0
  {"n":1.9} 读进 int 字段 → 1
  {"n":"7"} 读进 int 字段 → 7
[Jackson 2.21.5,再注册 JavaTimeModule 与 Jdk8Module]
  LocalDate.of(2026,10,23) → [2026,10,23]
  new java.util.Date(0) → 0
  Duration.ofSeconds(90) → 90.000000000
  Optional.of(1) / Optional.empty() → 1 / null
  字段声明顺序 b、a、c → {"b":"1","a":"2","c":3}
  LinkedHashMap 插入顺序 z、a、m → {"z":1,"a":2,"m":3}
  空对象 new Empty() → 异常 InvalidDefinitionException
  {"n":1,"zzz":2} 读进只有 n 的类 → 异常 UnrecognizedPropertyException
  {"n":null} 读进 int 字段 → 0
  {"n":1.9} 读进 int 字段 → 1
  {"n":"7"} 读进 int 字段 → 7
[Jackson 3.1.5,JsonMapper.builder().build()]
  LocalDate.of(2026,10,23) → "2026-10-23"
  new java.util.Date(0) → "1970-01-01T00:00:00.000Z"
  Duration.ofSeconds(90) → "PT1M30S"
  Optional.of(1) / Optional.empty() → 1 / null
  字段声明顺序 b、a、c → {"a":"2","b":"1","c":3}
  LinkedHashMap 插入顺序 z、a、m → {"z":1,"a":2,"m":3}
  空对象 new Empty() → {}
  {"n":1,"zzz":2} 读进只有 n 的类 → 1
  {"n":null} 读进 int 字段 → 异常 MismatchedInputException
  {"n":1.9} 读进 int 字段 → 1
  {"n":"7"} 读进 int 字段 → 7

Jackson 3 的默认:ISO 日期、字母序、未知属性不失败、null 读 int 失败。

Boot 4

实测 · 本机执行输出
  starter-web 带进来的 Jackson jar:jackson-annotations-2.21、jackson-core-3.1.5、jackson-databind-3.1.5、spring-boot-jackson-4.0.8、spring-boot-starter-jackson-4.0.8
  加上 spring-boot-jackson2 与 Jackson 2 databind 之后多出的 Jackson jar:jackson-core-2.21.5、jackson-databind-2.21.5、jackson-datatype-jdk8-2.21.5、jackson-datatype-jsr310-2.21.5、jackson-module-parameter-names-2.21.5、spring-boot-jackson2-4.0.8
  [默认]
    GET /d → 200 {"day":"2026-10-23","name":"x","nothing":null,"camelCase":1}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 400 {"timestamp":"2026-10-23T07:11:44.029Z","status":4
    bean tools.jackson.databind.ObjectMapper → [jacksonJsonMapper]
    bean tools.jackson.databind.json.JsonMapper → [jacksonJsonMapper]
    bean com.fasterxml.jackson.databind.ObjectMapper → (类不在 classpath 上)
  [spring.jackson.serialization.indent-output=true]
    GET /d → 200 {↵  "day" : "2026-10-23",↵  "name" : "x",↵  "nothing" : null,↵  "camelCase" : 1↵}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 400 {
  "timestamp" : "2026-10-23T07:11:45.444Z",
  "s
  [spring.jackson.default-property-inclusion=non_null]
    GET /d → 200 {"day":"2026-10-23","name":"x","camelCase":1}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 400 {"timestamp":"2026-10-23T07:11:46.862Z","status":4
  [spring.jackson.property-naming-strategy=SNAKE_CASE]
    GET /d → 200 {"day":"2026-10-23","name":"x","nothing":null,"camel_case":1}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 400 {"timestamp":"2026-10-23T07:11:48.263Z","status":4
  [spring.jackson.serialization.write-dates-as-timestamps=true] 启动失败:Reason: failed to convert java.lang.String to tools.jackson.databind.SerializationFeature (caused by java.lang.IllegalArgumentException: No enum constant tools.jackson.databind.SerializationFeature.write-dates-as-timestamps)
  [spring.jackson.datatype.datetime.write-dates-as-timestamps=true]
    GET /d → 200 {"day":[2026,10,23],"name":"x","nothing":null,"camelCase":1}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 400 {"timestamp":1792739511101,"status":400,"error":"B
  [spring.jackson.use-jackson2-defaults=true]
    GET /d → 200 {"day":"2026-10-23","name":"x","nothing":null,"camelCase":1}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 200 ok n=0
  [spring.jackson.deserialization.fail-on-unknown-properties=true]
    GET /d → 200 {"day":"2026-10-23","name":"x","nothing":null,"camelCase":1}
    POST /p {"n":1,"zzz":2} → 400 {"timestamp":"2026-10-23T07:11:55.451Z","status":400,"error";POST /p {"n":null} → 400 {"timestamp":"2026-10-23T07:11:55.463Z","status":4
  [加了 spring-boot-jackson2 与 Jackson 2]
    GET /d → 200 {"day":"2026-10-23","name":"x","nothing":null,"camelCase":1}
    POST /p {"n":1,"zzz":2} → 200 ok n=1;POST /p {"n":null} → 400 {"timestamp":"2026-10-23T07:11:57.979Z","status":4
    bean tools.jackson.databind.ObjectMapper → [jacksonJsonMapper]
    bean tools.jackson.databind.json.JsonMapper → [jacksonJsonMapper]
    bean com.fasterxml.jackson.databind.ObjectMapper → [jackson2ObjectMapper]

容器里只有 JsonMapper;大多数 spring.jackson.* 仍有效,write-dates-as-timestamps 要换位置;可用 use-jackson2-defaults 或 spring-boot-jackson2 过渡。

分步演示

Jackson 3
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Jackson 2.21.5 与 Jackson 3.1.5 的版本由 Spring Boot 4.0.8 的 BOM 管理;「20 段写法」每段都是单独的一个 .java 文件,逐个 javac 编译,不是一次编译整批;行为部分在同一份测试代码上分别用两套 jar 运行;Boot 部分是 starter-web 的最小应用,每个属性单独启动一次)
    • 没有验证:Jackson 3 的 JsonNode 新 API(asString、stringValue 等)的完整语义;自定义 Module 与 Serializer 迁移后的行为;spring.jackson.* 里除上面几个之外的属性;Jackson 3 的 builder 上 DateTimeFeature、DeserializationFeature 等的全部默认值;spring-boot-jackson2 的 spring.jackson2.* 属性的实际效果;WebFlux、Kafka、Redis 序列化器等其他使用 Jackson 的地方;性能差异(另有专门一篇)
    京ICP备2026067502号-1京公网安备11010502063487号