文档目标:为Java开发工程师提供从Spring Boot 3迁移到Spring Boot 4.0.0的完整指南,重点讲解Jackson版本升级的影响、配置变化、API调用差异,以及如何通过兼容层保证平滑过渡。
一、迁移概述
1.1 Spring Boot 4.0.0的核心变化
Spring Boot 4.0.0是一个重大版本升级,其中Jackson模块的主要变化包括:
1.2 升级影响范围
✅ 大部分代码无需修改(Spring Boot自动适配)
⚠️ 自定义Jackson配置需要调整
⚠️ 使用了Jackson内部API的代码需要重构
❌ 依赖Jackson 2的第三方库可能不兼容
二、Jackson 3 的主要变化
2.1 包名和 Maven 坐标变化
Jackson 2.x:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
Jackson 3.0:
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
import tools.jackson.databind.ObjectMapper; // ← 新包名
import tools.jackson.core.JacksonException; // ← 新异常类
⚠️ 例外:jackson-annotations 保持不变
<!-- 注解模块在Jackson 3 中仍使用旧的 Group ID -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
// 注解包名不变
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnore;
2.2 核心类重命名
2.3 Spring Boot类重命名
2.4 异常模型变化(重要!)
Jackson 2.x:使用受检异常(Checked Exception)
// Jackson 2: 必须显式捕获或声明抛出
public String serialize(Object obj) throws JsonProcessingException {
return objectMapper.writeValueAsString(obj);
}
try {
String json = objectMapper.writeValueAsString(data);
} catch (JsonProcessingException e) { // 必须捕获
log.error("序列化失败", e);
}
Jackson 3.0:使用非受检异常(Unchecked Exception)
// Jackson 3: 可选择是否捕获
public String serialize(Object obj) { // 不需要throws声明
return objectMapper.writeValueAsString(obj);
}
// 可以不捕获(让异常向上传播)
String json = objectMapper.writeValueAsString(data);
// 也可以选择捕获
try {
String json = objectMapper.writeValueAsString(data);
} catch (JacksonException e) { // 改为JacksonException
log.error("序列化失败", e);
}
迁移建议:
✅ 保留 try-catch 块(向后兼容)
✅ 异常类型从
JsonProcessingException改为JacksonException✅ 移除方法签名中的
throws声明(可选)
2.5 ObjectMapper 不可变性
Jackson 2.x:ObjectMapper可变
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true); // 直接修改
mapper.setSerializationInclusion(Include.NON_NULL); // 直接修改
Jackson 3.0:ObjectMapper不可变,使用Builder模式
// 方式 1: 使用 Builder
JsonMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(Include.NON_NULL)
.build();
// 方式 2: 从现有 Mapper 复制并修改
JsonMapper newMapper = mapper.rebuild()
.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();
2.6 Java 8模块内置
Jackson 2.x:需要单独添加依赖
<!-- Jackson 2: 需要显式添加 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <!-- Java 8 时间 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId> <!-- Optional 等 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId> <!-- 参数名 -->
</dependency>
Jackson 3.0:自动内置
<!-- Jackson 3: 自动包含,无需添加 -->
<!-- 以下功能已内置到 jackson-databind -->
<!-- - java.time 支持 (LocalDateTime, Instant 等) -->
<!-- - Optional 支持 (Optional<T>, OptionalInt 等) -->
<!-- - 构造函数参数名自动识别 -->
三、从Spring Boot 3到4的完整迁移步骤
3.1 步骤1:升级Spring Boot版本
<!-- pom.xml -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0</version> <!-- 从 3.x 升级到 4.0.0 -->
</parent>
3.2 步骤2:更新Import语句
如果您的代码直接使用了Jackson API:
// ❌ Spring Boot 3 / Jackson 2
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
// ✅ Spring Boot 4 / Jackson 3
import tools.jackson.databind.ObjectMapper; // 改包名
import tools.jackson.core.JacksonException; // 改异常类
import tools.jackson.core.type.TypeReference;
// ✅ 注解包名不变
import com.fasterxml.jackson.annotation.*; // 保持不变
3.3 步骤3:更新异常处理
// ❌ Jackson 2风格(受检异常)
public String toJson(Object obj) throws JsonProcessingException {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.error("序列化失败", e);
throw e;
}
}
// ✅ Jackson 3风格(非受检异常)
public String toJson(Object obj) { // 不需要throws声明
try {
return objectMapper.writeValueAsString(obj);
} catch (JacksonException e) { // 改为 JacksonException
log.error("序列化失败", e);
throw e; // 或者转换为业务异常
}
}
3.4 步骤4:更新ObjectMapper配置
// ❌ Jackson 2风格(直接修改)
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.registerModule(new JavaTimeModule()); // Java 8时间模块
return mapper;
}
// ✅ Jackson 3风格(Builder 模式)
@Bean
public JsonMapper jsonMapper() { // 使用JsonMapper
return JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_NULL)
// Java 8模块已内置,无需注册
.build();
}
3.5 步骤5:更新Spring Boot类注解
// ❌ Spring Boot 3
@JsonComponent
public class CustomSerializer extends JsonSerializer<MyObject> {
// ...
}
// ✅ Spring Boot 4
@JacksonComponent // 改名
public class CustomSerializer extends JsonSerializer<MyObject> {
// ...
}
3.6 步骤6:更新配置文件
# ❌ Spring Boot 3
spring:
jackson:
read:
enum-as-empty-string-lenient: true
write:
dates-as-timestamps: false
# ✅ Spring Boot 4
spring:
jackson:
json:
read: # 新增 json 层级
enum-as-empty-string-lenient: true
write: # 新增 json 层级
dates-as-timestamps: false
四、Jackson 2兼容性配置(过渡方案)
4.1 场景说明
当您的项目包含以下情况时,需要保留 Jackson 2 支持:
使用了依赖 Jackson 2 的第三方库(如Spring Cloud Gateway WebMVC)
暂时无法完成代码重构
需要渐进式迁移
4.2 Maven依赖配置
在需要 Jackson 2 兼容的模块中添加:
<dependencies>
<!-- 必需:Jackson 2兼容模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-jackson2</artifactId>
</dependency>
<!-- 示例:需要Jackson 2的第三方库 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId>
</dependency>
</dependencies>
重要说明:Jackson 2(com.fasterxml.jackson.*)和Jackson 3(tools.jackson.*)的包名不同(仅jackson-annotations保持一致),这意味着两个版本可以共存。但需要注意:
✅ 优点:不会出现同名类冲突,可以在同一项目中同时使用两个版本
⚠️ 风险:如果不小心混用(如Jackson 2的ObjectMapper处理Jackson 3的注解),会导致运行时错误
📦 包体积:同时引入两个版本会增加约3-4MB的依赖体积
最佳实践:尽量避免长期共存,仅在过渡期使用兼容方案。
五、性能对比:Jackson 2 vs Jackson 3(JMH基准测试)
本节通过JMH(Java Microbenchmark Harness)基准测试,展示Jackson 2与Jackson 3的性能差异。
5.1 简化的 JMH 测试代码示例
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 2)
@Measurement(iterations = 5, time = 3)
@Fork(2)
@Threads(4)
public class Jackson2vs3Benchmark {
private ObjectMapper jackson2Mapper;
private JsonMapper jackson3DefaultMapper;
private JsonMapper jackson3TunedMapper;
private TestData testData;
@Setup
public void setup() {
// Jackson 2
jackson2Mapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// Jackson 3 默认配置
jackson3DefaultMapper = JsonMapper.builder()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
// Jackson 3 优化配置(使用 ThreadLocal 回收池)
jackson3TunedMapper = JsonMapper.builder(
JsonFactory.builder()
.recyclerPool(JsonRecyclerPools.threadLocalPool())
.build()
).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build();
testData = new TestData(/* 测试数据 */);
}
@Benchmark
public String jackson2_serialize() throws Exception {
return jackson2Mapper.writeValueAsString(testData);
}
@Benchmark
public String jackson3_default_serialize() throws Exception {
return jackson3DefaultMapper.writeValueAsString(testData);
}
@Benchmark
public String jackson3_tuned_serialize() throws Exception {
return jackson3TunedMapper.writeValueAsString(testData);
}
}
5.2 实测性能数据
测试环境:JDK 25,WSL2,4线程,-Xms2G -Xmx2G,测试对象包含 UUID、LocalDateTime、Optional、List 和嵌套对象。
Benchmark Mode Cnt Score Error Units
==================================================================================
jackson2_serialize thrpt 10 7297.855 ± 474.856 ops/ms
jackson2_deserialize thrpt 10 2431.000 ± 67.187 ops/ms
jackson3_default_serialize thrpt 10 4631.573 ± 548.454 ops/ms
jackson3_default_deserialize thrpt 10 2280.646 ± 49.615 ops/ms
jackson3_threadLocal_serialize thrpt 10 6614.307 ± 468.045 ops/ms
jackson3_threadLocal_deserialize thrpt 10 2350.067 ± 141.227 ops/ms
5.3 性能对比分析
关键结论:
⚠️ Jackson 3 默认配置使用deque-based RecyclerPool,导致序列化性能下降约 36.5%
✅ 启用threadLocalPool 后,性能可恢复到Jackson 2的 90-97%
📊 反序列化性能受影响较小(约6%),无论是否优化差异都不大
🎯 对于高并发/低延迟服务,强烈建议使用优化配置
5.4 性能优化配置示例
import tools.jackson.core.json.JsonFactory;
import tools.jackson.core.util.JsonRecyclerPools;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public JsonMapper jsonMapper() {
// 使用 threadLocalPool 获得接近 Jackson 2 的性能
JsonFactory factory = JsonFactory.builder()
.recyclerPool(JsonRecyclerPools.threadLocalPool())
.build();
return JsonMapper.builder(factory).build();
}
}
六、总结
6.1 迁移检查清单
在完成 Jackson 2 到 Jackson 3 的迁移后,请确认以下检查点:
代码层面:
[ ] 已更新所有
import语句(com.fasterxml..jackson.*→tools.jackson.*)[ ] 异常处理已从
JsonProcessingException改为JacksonException[ ]
ObjectMapper配置改用Builder模式(JsonMapper.builder())[ ] Spring Boot注解已更新(
@JsonComponent→@JacksonComponent)[ ] 配置文件YAML层级已调整(增加
json:层级)
依赖管理:
[ ] Spring Boot版本已升级到 4.0.0
[ ] 如需兼容Jackson 2,已添加
spring-boot-jackson2依赖[ ] 移除了手动添加的Java 8相关模块依赖(已内置)