详解:Jackson ObjectMapper 字段可见性配置
详解:Jackson ObjectMapper 字段可见性配置
/**
* JSON消息转换器
* 不使用任何命名策略转换,保持字段名原样
*
* @return {@link Jackson2JsonMessageConverter }
*/
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
// 确保字段可见性
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
// 禁用未知属性检测,避免因字段名问题导致序列化失败
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return new Jackson2JsonMessageConverter(objectMapper);
}
这段代码是在配置 RabbitMQ 消息转换器中的 JSON 序列化规则。
📌 核心概念:什么是字段可见性(Visibility)?
在 Java 中,对象序列化时,Jackson 需要知道哪些属性可以被读取和写入。字段可见性就是告诉 Jackson:
- 哪些字段可以被访问
- 通过什么方式访问(直接字段、getter 方法、setter 方法等)
🔍 逐行详解
第 1 行:
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
作用: 允许 Jackson 直接访问任何字段(包括 private 字段)
详细解释:
PropertyAccessor.FIELD:表示直接访问类的字段(不是通过 getter/setter 方法)JsonAutoDetect.Visibility.ANY:表示"任何可见性级别",包括private、protected、public等- 效果: 即使字段是
private的,没有 getter 方法,Jackson 也能直接读取和写入
示例:
public class User {
private String name; // 即使是 private,没有 getter,也能被序列化
}
// 序列化结果:{"name": "张三"}
第 2 行:
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
作用: 禁用通过 getter 方法访问属性
详细解释:
PropertyAccessor.GETTER:表示通过 getter 方法(如getName())访问属性JsonAutoDetect.Visibility.NONE:表示"完全不检测"- 效果: Jackson 不会调用任何 getter 方法来获取属性值
为什么要这样做?
- 避免重复序列化:如果同时开启 FIELD 和 GETTER,一个属性可能被序列化两次
- 避免逻辑干扰:有些 getter 方法包含业务逻辑,不希望被序列化触发
示例对比:
public class User {
private String name;
public String getName() {
return "Hello, " + name; // getter 中有业务逻辑
}
public String getUpperCaseName() {
return name.toUpperCase(); // 这是计算属性,不是真实字段
}
}
// 如果开启 GETTER 可见性:
// - name 会被序列化两次(一次从字段,一次从 getName())
// - getUpperCaseName() 也会被当作属性序列化
// 关闭 GETTER 后:只序列化 name 字段本身
第 3 行:
objectMapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
作用: 禁用通过 setter 方法设置属性
详细解释:
PropertyAccessor.SETTER:表示通过 setter 方法(如setName())设置属性值JsonAutoDetect.Visibility.NONE:完全不检测- 效果: 反序列化时,Jackson 不会调用 setter 方法,而是直接设置字段值
为什么要这样做?
- 性能考虑:直接设置字段比调用方法更快
- 避免副作用:有些 setter 方法包含验证逻辑或副作用,不希望被反序列化触发
示例:
public class User {
private Integer age;
public void setAge(Integer age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄不合法"); // setter 中有验证
}
this.age = age;
}
}
// 关闭 SETTER 可见性后:
// 反序列化时直接给 age 字段赋值,绕过验证逻辑
第 4 行:
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
作用: 禁用通过 is-getter 方法访问布尔属性
详细解释:
PropertyAccessor.IS_GETTER:特指布尔类型的 getter 方法,以is开头(如isActive())JsonAutoDetect.Visibility.NONE:完全不检测- 效果: 不会调用
isXxx()方法来获取布尔值
示例:
public class User {
private boolean active;
public boolean isActive() { // 这是 is-getter
return active;
}
}
// 关闭 IS_GETTER 后:直接从 active 字段读取值,不调用 isActive() 方法
🎯 整体配置的效果
这套配置的核心思想是:
只通过直接字段访问进行序列化和反序列化,完全绕过所有方法
优势:
- ✅ 保持字段原名:字段名是什么,JSON 中就是什么,不会被命名策略转换
- ✅ 性能更好:直接访问字段比调用方法快
- ✅ 避免副作用:不会触发 getter/setter 中的业务逻辑
- ✅ 行为可预测:不会有意外的属性被序列化(如计算属性)
- ✅ 简化调试:序列化过程更简单,更容易排查问题
适用场景:
- RabbitMQ 消息传递(如本例)
- 简单的数据传输对象(DTO)
- 需要精确控制序列化行为的场景
📊 配置前后对比
假设有这样一个类:
public class Message {
private String content;
private Integer type;
public String getContent() {
return "[加密]" + content; // getter 中添加了前缀
}
public void setContent(String content) {
this.content = content.toUpperCase(); // setter 转大写
}
}
不同配置下的行为:
| 配置 | 序列化结果 | 反序列化结果 |
|---|---|---|
| 默认配置(开启 GETTER/SETTER) | {"content":"[加密] hello"} |
content = "HELLO" |
| 当前配置(只开 FIELD) | {"content":"hello"} |
content = "hello" |
可以看到,当前配置绕过了所有方法逻辑,直接操作原始字段值。
💡 实际应用建议
这种配置特别适合:
- ✅ 消息队列通信:需要简单、快速、可预测的序列化
- ✅ 内部系统交互:不需要复杂的验证和转换
- ✅ 性能敏感场景:减少方法调用的开销
不适合:
- ❌ 对外 API:可能需要 getter 中的业务逻辑
- ❌ 需要数据验证:绕过了 setter 中的验证逻辑
- ❌ 计算属性:getter 计算的值不会被序列化
本文来自博客园,作者:Micky233,转载请注明原文链接:https://www.cnblogs.com/geek233/p/19795399

浙公网安备 33010602011771号