1. 概述
在本篇教程中,我们将介绍两种使用 Jackson JSON 处理库来反序列化不可变 Java 对象的方式。
2. 为什么使用不可变对象?
不可变对象(Immutable Object) 是指从创建之初起其状态就不再发生变化的对象。也就是说,无论调用该对象的哪个方法,它的行为始终保持一致。
在设计多线程环境下的系统时,不可变对象非常有用,因为它们天然具备线程安全特性。
此外,在处理外部输入数据时(如用户输入或从存储中读取的数据),不可变对象也非常重要,因为我们需要保护这些数据不被意外修改。
接下来我们看看如何对不可变对象进行反序列化。
3. 使用公共构造函数
以 Employee
类为例,它包含两个必填字段:id
和 name
。因此我们可以定义一个全参数公共构造函数,其参数与对象字段一一对应:
public class Employee {
private final long id;
private final String name;
public Employee(long id, String name) {
this.id = id;
this.name = name;
}
// getters
}
这样,在对象创建时就能完成所有字段的初始化。由于字段使用了 final
修饰符,后续也无法修改其值。
为了让 Jackson 能够反序列化这个类,我们只需为构造函数添加几个 注解:
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Employee(@JsonProperty("id") long id, @JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
我们来详细解释一下这些注解的作用。
首先,@JsonCreator
告诉 Jackson 反序列化器应该使用这个构造函数来进行反序列化。
该注解支持两种模式:PROPERTIES
和 DELEGATING
。
✅ 当我们使用全参数构造函数时,推荐使用 PROPERTIES
模式;
⚠️ 而 DELEGATING
更适用于单参数构造函数。
然后,我们需要为每个构造函数参数加上 @JsonProperty
注解,并指定对应的属性名。这一步要特别注意,属性名必须与序列化时使用的名称完全一致。
下面是一个简单的单元测试示例,用于验证 Employee
对象的反序列化:
String json = "{\"name\":\"Frank\",\"id\":5000}";
Employee employee = new ObjectMapper().readValue(json, Employee.class);
assertEquals("Frank", employee.getName());
assertEquals(5000, employee.getId());
4. 使用私有构造函数和 Builder 模式
有时候对象会包含一些可选字段。例如,Person
类有一个可选的 age
字段:
public class Person {
private final String name;
private final Integer age;
// getters
}
当字段较多且存在大量可选字段时,直接使用构造函数会导致参数膨胀,代码也会变得臃肿难读。
这时候就可以借助经典的 Builder 模式 来解决这个问题。
我们先声明一个私有的全参数构造函数以及一个 Builder
静态内部类:
private Person(String name, Integer age) {
this.name = name;
this.age = age;
}
static class Builder {
String name;
Integer age;
Builder withName(String name) {
this.name = name;
return this;
}
Builder withAge(Integer age) {
this.age = age;
return this;
}
public Person build() {
return new Person(name, age);
}
}
为了让 Jackson 能够识别并使用这个 Builder,我们需要添加两个注解:
- 在类上使用
@JsonDeserialize(builder = Person.Builder.class)
,指明 Builder 类的完整类名; - 在 Builder 类上使用
@JsonPOJOBuilder
注解。
@JsonDeserialize(builder = Person.Builder.class)
public class Person {
//...
@JsonPOJOBuilder
static class Builder {
//...
}
}
💡 注:你可以自定义 Builder 中的方法命名规则。
buildMethodName
默认是"build"
,表示最终构建对象的方法名;withPrefix
默认是"with"
,表示设置属性的方法前缀。
所以我们在上面的例子中没有显式指定这两个参数。
下面是用于测试 Person
对象反序列化的单元测试:
String json = "{\"name\":\"Frank\",\"age\":50}";
Person person = new ObjectMapper().readValue(json, Person.class);
assertEquals("Frank", person.getName());
assertEquals(50, person.getAge().intValue());
5. 总结
在这篇文章中,我们介绍了如何使用 Jackson 库来反序列化不可变对象。
文中提到的所有代码都可以在 GitHub 上找到。