序列化 之 Jackson(七)六大接口
SpringBoot Jackson 六大序列化接口详解
先理清核心关系、底层原理、区别,再给完整可运行示例:
JsonSerializer/JsonDeserializer:固定上下文序列化/反序列化器ContextualSerializer/ContextualDeserializer:上下文感知,能读取字段注解、类注解、属性上下文,动态生成最终序列化器BeanSerializerModifier/BeanDeserializerModifier:修改器(Modifier),作用于整个 Java Bean 序列化 / 反序列化构建流程,属于全局 / 类级别的流程拦截、修改、替换。
核心场景:需要根据注解参数动态规则(如脱敏级别、字典映射、日期格式、加密强度),必须用「上下文接口」
一基础使用
一、核心接口原理 & 区别
1. 基础接口:无上下文
① JsonSerializer<T>
public abstract class JsonSerializer<T> {
// 固定序列化逻辑
public abstract void serialize(T value, JsonGenerator gen, SerializerProvider provider);
}
- 特点:全局固定逻辑,无法感知当前字段注解、属性配置
- 缺点:同一类型只能一套规则,无法差异化(比如手机号有的脱敏、有的不脱敏)
② JsonDeserializer<T>
public abstract class JsonDeserializer<T> {
public abstract T deserialize(JsonParser p, DeserializationContext ctxt);
}
- 固定反序列化逻辑,无法读取字段注解参数
2. 上下文接口:动态上下文
① ContextualSerializer 上下文序列化
public interface ContextualSerializer {
// 关键:根据【字段注解、属性、类信息】创建定制化Serializer
JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property);
}
执行流程:
- Jackson 初始化序列化器
- 调用
createContextual,传入当前字段BeanProperty - 从
BeanProperty获取字段注解、字段名、所属类 - 根据注解参数,动态new 自定义序列化器实例
- 最终使用返回的序列化器执行
serialize
② ContextualDeserializer 上下文反序列化
public interface ContextualDeserializer {
JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property);
}
- 同理:反序列化时读取字段注解,动态生成反序列化器
3. 四者关系总结
| 接口 | 上下文感知 | 能否读取字段注解 | 适用场景 |
|---|---|---|---|
| JsonSerializer | ❌ | ❌ | 全局统一规则 |
| JsonDeserializer | ❌ | ❌ | 全局统一反序列化 |
| ContextualSerializer | ✅ | ✅ | 动态规则:脱敏、字典、自定义格式 |
| ContextualDeserializer | ✅ | ✅ | 动态解析:自定义枚举、解密、特殊解析 |
开发高频组合:
自定义序列化器 = 继承JsonSerializer+ 实现ContextualSerializer
自定义反序列化器 = 继承JsonDeserializer+ 实现ContextualDeserializer
二、实战场景1:ContextualSerializer 动态脱敏
步骤1:自定义注解(携带动态参数)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {
// 脱敏类型:手机号、身份证、姓名
SensitiveType type() default SensitiveType.NAME;
}
// 脱敏枚举
enum SensitiveType {
NAME, PHONE, ID_CARD
}
步骤2:上下文序列化器(核心)
同时实现:JsonSerializer<String> + ContextualSerializer
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
public class SensitiveSerializer
extends JsonSerializer<String>
implements ContextualSerializer {
// 保存当前字段的脱敏类型(上下文传递)
private SensitiveType sensitiveType;
// 空构造:Jackson 反射实例化
public SensitiveSerializer() {}
// 私有构造:上下文创建时传入动态参数
private SensitiveSerializer(SensitiveType type) {
this.sensitiveType = type;
}
// 【上下文核心方法】读取字段注解,动态生成序列化器
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
// 获取字段上的 @Sensitive 注解
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (annotation != null) {
// 根据注解参数,返回定制化序列化器
return new SensitiveSerializer(annotation.type());
}
// 无注解,返回原序列化器(不处理)
return prov.findValueSerializer(property.getType(), property);
}
// 真正序列化逻辑
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws Exception {
if (value == null) {
gen.writeNull();
return;
}
String result = switch (sensitiveType) {
case PHONE -> phoneMask(value);
case ID_CARD -> idCardMask(value);
case NAME -> nameMask(value);
};
gen.writeString(result);
}
// 脱敏工具方法
private String nameMask(String name) {
if (name.length() <= 1) return name;
return name.charAt(0) + "**";
}
private String phoneMask(String phone) {
if (phone.length() != 11) return phone;
return phone.substring(0,3) + "****" + phone.substring(7);
}
private String idCardMask(String id) {
if (id.length() < 10) return id;
return id.substring(0,6) + "********" + id.substring(14);
}
}
步骤3:实体类使用
public class User {
private Long id;
@Sensitive(type = SensitiveType.NAME)
private String username;
@Sensitive(type = SensitiveType.PHONE)
private String phone;
@Sensitive(type = SensitiveType.ID_CARD)
private String idCard;
// getter/setter
}
步骤4:指定序列化器生效
两种方式:
- 字段直接指定
@JsonSerialize(using = SensitiveSerializer.class)
@Sensitive(type = SensitiveType.PHONE)
private String phone;
- 全局注册(统一拦截 String 类型)
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new SensitiveSerializer());
return new ObjectMapper().registerModule(module);
}
}
效果
{
"id": 1,
"username": "张**",
"phone": "138****1234",
"idCard": "110101********1234"
}
三、实战场景2:ContextualDeserializer 动态反序列化
需求:前端传加密字符串,根据字段注解标记,自动解密;无注解不处理
1. 自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Decrypt {
// 解密算法
String algorithm() default "AES";
}
2. 上下文反序列化器
继承 JsonDeserializer + 实现 ContextualDeserializer
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
public class DecryptDeserializer
extends JsonDeserializer<String>
implements ContextualDeserializer {
private String algorithm;
public DecryptDeserializer() {}
private DecryptDeserializer(String algorithm) {
this.algorithm = algorithm;
}
// 上下文:读取 @Decrypt 注解
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
Decrypt decrypt = property.getAnnotation(Decrypt.class);
if (decrypt != null) {
return new DecryptDeserializer(decrypt.algorithm());
}
return ctxt.findContextualValueDeserializer(property.getType(), property);
}
// 反序列化+解密
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws Exception {
String cipherText = p.getText();
if (cipherText == null) return null;
// 模拟不同算法解密
return switch (algorithm) {
case "AES" -> aesDecrypt(cipherText);
case "RSA" -> rsaDecrypt(cipherText);
default -> cipherText;
};
}
// 模拟解密
private String aesDecrypt(String text){return "AES解密:" + text;}
private String rsaDecrypt(String text){return "RSA解密:" + text;}
}
3. 实体使用
public class UserDTO {
@JsonDeserialize(using = DecryptDeserializer.class)
@Decrypt(algorithm = "AES")
private String password;
@JsonDeserialize(using = DecryptDeserializer.class)
@Decrypt(algorithm = "RSA")
private String bankNo;
// getter/setter
}
四、纯基础版:JsonSerializer 无上下文示例
无上下文,全局固定逻辑,无法读取注解:
// 固定:所有String都加前缀
public class SimpleStrSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws Exception {
gen.writeString("prefix_" + value);
}
}
缺点:全局统一逻辑,不能针对单个字段差异化
五、核心底层执行流程(必看)
序列化流程
- Spring MVC 响应结果 → Jackson 消息转换器
- 查找字段上
@JsonSerialize指定的序列化器 - 判断是否实现
ContextualSerializer- 是 → 执行
createContextual,读取注解 → 生成定制序列化器 - 否 → 直接使用原始序列化器
- 是 → 执行
- 调用
serialize方法输出 JSON
反序列化流程
- 前端 JSON 入参 → Jackson 解析
- 查找
@JsonDeserialize指定反序列化器 - 实现
ContextualDeserializer→ 执行createContextual - 调用
deserialize完成解析
六、关键要点总结
-
单纯 JsonSerializer/Deserializer
- 无上下文、无注解读取能力
- 适合:全局统一格式化、简单转换
-
Contextual 上下文接口
- 核心方法:
createContextual - 核心对象:
BeanProperty(获取字段注解、字段类型、类信息) - 适合:脱敏、加解密、动态日期格式、枚举自定义解析、字典翻译
- 核心方法:
-
SpringBoot 整合
- 配合
@JsonSerialize/@JsonDeserialize注解使用 - 或通过
SimpleModule全局注册,批量处理类型
- 配合
-
生产常用组合
- 脱敏、数据加解密、数据翻译:必用
ContextualSerializer - 枚举统一序列化、日期全局格式化:可用基础
JsonSerializer
- 脱敏、数据加解密、数据翻译:必用
二、扩展
Jackson 中 BeanSerializerModifier、BeanDeserializerModifier 详解 + 与 JsonSerializer/JsonDeserializer 区别
一、整体前置说明
两者都属于 Jackson 2.x 扩展点,用于自定义序列化/反序列化逻辑,但层级、作用范围、使用场景完全不同:
BeanSerializerModifier/BeanDeserializerModifier:修改器(Modifier),作用于整个 Java Bean 序列化/反序列化构建流程,属于全局/类级别的流程拦截、修改、替换。JsonSerializer<T>/JsonDeserializer<T>:单独序列化/反序列化器,作用于单个字段/单个类型,是最终执行序列化逻辑的最小单元。
二、BeanSerializerModifier 详解(序列化修改器)
1. 类定位
com.fasterxml.jackson.databind.ser.BeanSerializerModifier
职责:在 Jackson 为一个 Java Bean 生成序列化器(BeanSerializer)的全过程中做拦截、改造、替换。
属于 Module 级扩展,需要注册到 SimpleModule 生效,作用域:整个 Bean(类)。
2. 核心生命周期方法(执行顺序)
(1)modifyProperties()
时机:解析完 Bean 所有字段(getter/field)后、生成字段序列化器之前。
用途:
- 动态增/删/改 Bean 的序列化字段
- 修改字段名称、过滤敏感字段、动态隐藏字段
- 调整字段的注解、元数据
(2)changeProperties()
时机:modifyProperties 之后,进一步批量调整字段列表。
一般和 modifyProperties 配合做字段重组。
(3)modifySerializer()
时机:Jackson 已经构建好默认 BeanSerializer 之后。
用途:
- 对整个 Bean 的序列化器进行包装、替换
- 全局统一修改该类所有字段的序列化行为
- 替换整个 Bean 的最终序列化实现
(4)modifyArraySerializer()
针对数组/集合类型的 Bean 序列化器修改。
3. 基础使用示例(过滤敏感字段)
1)自定义 Modifier
public class CustomBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> modifyProperties(
SerializationConfig config,
BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
// 过滤掉名为 password 的字段(全局隐藏密码)
return beanProperties.stream()
.filter(writer -> !"password".equals(writer.getName()))
.collect(Collectors.toList());
}
}
2)注册到 Module(全局生效)
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new CustomBeanSerializerModifier());
mapper.registerModule(module);
4. 典型使用场景
- 全局统一屏蔽敏感字段(密码、手机号、身份证)
- 动态根据环境/权限显示/隐藏字段
- 批量修改一批类的字段名、字段顺序
- 统一给某类 Bean 追加额外 JSON 字段
- 包装默认 BeanSerializer 做全局切面(如统一加日志、加签名)
三、BeanDeserializerModifier 详解(反序列化修改器)
1. 类定位
com.fasterxml.jackson.databind.deser.BeanDeserializerModifier
和序列化修改器对称,拦截 Bean 反序列化器的构建流程,同样通过 SimpleModule 注册,作用域:整个 Bean。
2. 核心方法
(1)modifyProperties()
解析 JSON 字段、映射到 Java Bean 属性阶段,可:
- 删除不需要反序列化的字段
- 重命名字段映射关系
- 忽略非法多余字段
(2)modifyDeserializer()
Jackson 生成默认 BeanDeserializer 后,可替换/包装整个反序列化器。
(3)modifyEnumDeserializer() / modifyArrayDeserializer()
针对枚举、数组/集合类型单独处理。
3. 简单示例(忽略指定字段反序列化)
public class CustomBeanDeserializerModifier extends BeanDeserializerModifier {
@Override
public List<SettableBeanProperty> modifyProperties(
DeserializationConfig config,
BeanDescription beanDesc,
List<SettableBeanProperty> properties) {
// 不让 age 字段参与反序列化
return properties.stream()
.filter(prop -> !"age".equals(prop.getName()))
.collect(Collectors.toList());
}
}
注册方式同序列化 Modifier:
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new CustomBeanDeserializerModifier());
mapper.registerModule(module);
4. 典型场景
- 全局忽略前端传过来的多余脏字段
- 动态调整 JSON → Java 字段映射关系
- 统一对一类 Bean 做反序列化前置校验、数据清洗
- 替换整个 Bean 的反序列化逻辑
四、JsonSerializer / JsonDeserializer 详解(字段/类型级序列化器)
1. 类定位
- 序列化:
com.fasterxml.jackson.databind.JsonSerializer<T> - 反序列化:
com.fasterxml.jackson.databind.JsonDeserializer<T>
最小执行单元:只负责单个类型 / 单个字段的序列化/反序列化逻辑。
使用方式两种:
- 注解方式:直接加在字段/类上(局部生效)
- Module 全局注册:绑定某个 Java 类型(全局生效)
2. JsonSerializer 核心方法
@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 手写该字段/类型的 JSON 输出逻辑
}
3. JsonDeserializer 核心方法
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// 手写 JSON 解析、转 Java 对象逻辑
}
4. 使用示例(日期格式化)
自定义序列化器
public class DateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(SDF.format(value));
}
}
注解使用(字段级别)
public class User {
@JsonSerialize(using = DateSerializer.class)
private Date createTime;
}
全局注册(整个项目 Date 都走该序列化器)
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new DateSerializer());
mapper.registerModule(module);
5. 典型场景
- 单个字段特殊格式化(日期、枚举、金额、加密字段)
- 字段值转换(Long → String、脱敏、截断)
- 枚举自定义输出文本
- 一对一、精准控制某一个类型/某一个字段的解析规则
五、核心区别对比(重点)
1. 维度总表
| 对比项 | BeanSerializerModifier / BeanDeserializerModifier | JsonSerializer / JsonDeserializer |
|---|---|---|
| 层级 | Bean 级别(类级别)流程修改器 | 字段/类型级别 执行器 |
| 作用对象 | 整个 Java Bean 的所有字段 | 单个字段 / 单个 Java 类型 |
| 核心能力 | 拦截序列化器/反序列化器的构建过程 增删改字段、替换整个Bean解析器 |
手写最终序列化/反序列化执行逻辑 |
| 生效时机 | Jackson 构建解析器之前/之中/之后 | 真正读写 JSON 数据阶段 |
| 使用粒度 | 粗粒度、批量、全局一类 Bean | 细粒度、精准、单个字段/类型 |
| 常用注册方式 | 统一注册到 SimpleModule(全局) |
注解 + Module 全局注册均可 |
| 能否增删字段 | ✅ 可以动态增、删、改 Bean 字段 | ❌ 只能改当前字段值,不能增减字段 |
2. 执行顺序(整条调用链)
以序列化为例:
1. ObjectMapper 开始序列化对象
2. 触发 BeanSerializerModifier
→ modifyProperties(修改字段列表)
→ modifySerializer(替换整个Bean序列化器)
3. 进入最终 BeanSerializer
4. 遍历每个字段 → 调用该字段上 **JsonSerializer** 执行序列化
总结链路:
Modifier(流程管控、改字段) → JsonSerializer(真正干活、写JSON)
3. 选型建议(业务怎么选)
场景1:需要隐藏/删除/新增字段、批量调整一批类的字段结构
👉 用 BeanSerializerModifier / BeanDeserializerModifier
例:全局统一屏蔽所有实体的 password、secret 字段。
场景2:只需要对某个字段/某个类型做格式转换、脱敏、格式化
👉 用 JsonSerializer / JsonDeserializer
例:日期格式化、手机号脱敏、枚举转文本、BigDecimal 保留小数。
场景3:既要改字段结构,又要改字段值
👉 两者组合使用:
Modifier 负责过滤/重组字段,JsonSerializer 负责字段值格式化。
4. 关键补充误区
-
Modifier 不能替代 JsonSerializer
Modifier 是“包工头”,负责安排有哪些字段;JsonSerializer 是“工人”,负责把字段值写成 JSON。 -
JsonSerializer 无法增减字段
它只能处理当前字段的值,不能决定这个字段要不要出现在 JSON 里。 -
修饰器是全局 Module 扩展,一旦注册对所有匹配的 Bean生效;
注解式 JsonSerializer 只对当前字段/当前类生效,局部优先级更高。
六、一句话总结
- Modifier:管整个 Bean 的字段列表、解析器整体,做流程和结构改造;
- JsonSerializer/Deserializer:管单个字段/类型的值转换,做数据内容改造。
两者常搭配使用,各司其职。
三 执行顺序
结合 Jackson 执行流程、注册方式、注解规则、优先级,分三类讲清生效条件、触发时机、执行链路、优先级,附带示例与使用边界。
一、整体前置
四类组件归属:
BeanSerializerModifier/BeanDeserializerModifier:Bean 构建阶段修改器(全局 Module 扩展)JsonSerializer/JsonDeserializer:基础序列化/反序列化器(字段/类型级别)ContextualSerializer/ContextualDeserializer:带上下文感知的序列化/反序列化器(继承自上面基础接口,支持读取注解属性)
生效核心规则:先执行 Modifier(改结构/改解析器)→ 再执行 普通/上下文 Serializer(处理字段值)
二、BeanSerializerModifier / BeanDeserializerModifier 生效机制
1. 核心前提
必须注册到 SimpleModule,再注册进 ObjectMapper,无注解用法,全局生效。
作用阶段:Jackson 创建 BeanSerializer / BeanDeserializer 实例的构建阶段。
2. 生效步骤(序列化侧)
- 初始化
SimpleModule,调用setSerializerModifier()绑定自定义 Modifier module注册到ObjectMapper- 当 ObjectMapper 第一次序列化某个 Java Bean 时:
- Jackson 扫描 Bean 所有属性、注解
- 触发
BeanSerializerModifier生命周期方法:modifyProperties→changeProperties→modifySerializer - 动态增删字段、重命名、替换整个 Bean 序列化器
- 构建完成后缓存 BeanSerializer,后续同类型 Bean 直接走缓存,不再重复执行 Modifier
3. 反序列化侧(对称逻辑)
- 同样通过
SimpleModule.setDeserializerModifier()注册 - 第一次反序列化某 Bean 时触发:
modifyProperties→modifyDeserializer - 调整可反序列化字段、替换整体反序列化器,结果缓存
4. 生效范围 & 优先级
- 范围:对所有 Java Bean 统一生效,无针对单个类的注解开关
- 优先级:早于所有字段级 Serializer/Deserializer
- 关闭/局部失效:无法单独对某个类禁用,如需局部控制,在 Modifier 内部通过
beanDesc判断类名做逻辑拦截
最简注册代码
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
// 注册序列化修改器
module.setSerializerModifier(new CustomBeanSerializerModifier());
// 注册反序列化修改器
module.setDeserializerModifier(new CustomBeanDeserializerModifier());
mapper.registerModule(module);
三、JsonSerializer / JsonDeserializer 生效机制
分两种使用方式:注解局部生效、Module 全局类型绑定生效,二者可并存。
方式1:注解方式(局部,字段/类级别)
1)@JsonSerialize / @JsonDeserialize 注解
直接标注在 类、字段、getter/setter 上。
// 字段级别(最常用)
public class User {
@JsonSerialize(using = MyStrSerializer.class)
private String phone;
@JsonDeserialize(using = MyDateDeserializer.class)
private Date createTime;
}
生效逻辑
- Jackson 构建 Bean 属性时,扫描字段/类上的
@JsonSerialize/@JsonDeserialize - 为当前属性单独绑定指定的 Serializer/Deserializer
- 序列化/反序列化运行阶段,直接调用该实例执行逻辑
- 范围:仅当前标注的字段/类生效,其他同类型字段不受影响
方式2:Module 全局绑定(类型级别)
对整个 Java 类型统一绑定解析器,全局所有该类型字段都生效。
SimpleModule module = new SimpleModule();
// 全局:所有 String 类型都使用该序列化器
module.addSerializer(String.class, new MyStrSerializer());
// 全局:所有 Date 类型都使用该反序列化器
module.addDeserializer(Date.class, new MyDateDeserializer());
mapper.registerModule(module);
生效逻辑
- Module 注册后,ObjectMapper 维护「Java类型 → 解析器」映射表
- 遇到对应类型字段,自动匹配全局绑定的解析器
- 范围:项目内所有该类型字段
优先级规则(重要)
注解局部解析器 > Module全局类型解析器
同一个字段:优先使用注解指定的,全局绑定失效。
执行时机
在 Modifier 执行完成、字段列表确定后,遍历字段时执行。
四、ContextualSerializer / ContextualDeserializer 生效机制
基础说明
- 是 JsonSerializer / JsonDeserializer 的子接口,额外多一个
createContextual方法 - 核心作用:读取字段/类上的自定义注解属性,实现「同一个解析器,不同字段不同行为」
- 典型场景:根据注解参数动态格式化日期、动态脱敏规则、动态枚举转换
1. 接口结构
// 序列化
public interface ContextualSerializer extends JsonSerializer<Object> {
// 上下文创建方法:解析注解、生成真正使用的 Serializer
JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException;
}
// 反序列化
public interface ContextualDeserializer extends JsonDeserializer<Object> {
JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException;
}
2. 完整生效流程(分两步)
阶段1:上下文初始化(Bean 构建阶段,只执行一次 + 缓存)
- 字段标注
@JsonSerialize(using = XxxContextualSerializer.class) - Jackson 扫描到该解析器实现了
ContextualSerializer - 调用
createContextual():- 通过
BeanProperty获取当前字段上的自定义注解、注解参数 - 根据参数创建/配置真正干活的 Serializer 实例并缓存
- 通过
- 此阶段和 Modifier 同属「解析器构建期」
阶段2:实际序列化/反序列化(运行阶段)
- 复用上一步缓存好的、带上下文配置的解析器实例
- 调用父接口
serialize()/deserialize()执行业务逻辑 - 不会重复执行 createContextual(同字段全局缓存)
3. 生效方式
和普通 JsonSerializer 完全一致,支持两种用法:
- 注解绑定(主流):
@JsonSerialize(using = 实现类.class)标注在字段/类上 - Module 全局绑定:不推荐,因为全局无法区分字段注解,失去上下文意义
4. 示例(简易上下文序列化器)
自定义注解 + 上下文解析器:
// 自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {
int maskLen() default 3;
}
// 上下文序列化器
public class SensitiveSerializer implements ContextualSerializer {
private int maskLength;
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
// 读取字段上的 @Sensitive 注解参数
Sensitive anno = property.getAnnotation(Sensitive.class);
if (anno != null) {
this.maskLength = anno.maskLen();
}
return this;
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 使用上下文参数做脱敏
String str = String.valueOf(value);
String res = str.substring(0, maskLength) + "****";
gen.writeString(res);
}
}
// 使用
public class User {
@Sensitive(maskLen = 2)
@JsonSerialize(using = SensitiveSerializer.class)
private String idCard;
}
5. 优先级 & 特殊规则
- 优先级:和普通
JsonSerializer一致:注解 > 全局Module绑定 - 执行顺序:
BeanSerializerModifier→createContextual(上下文初始化)→serialize(实际序列化) - 缓存特性:
每个字段对应一个独立上下文实例,同字段复用缓存,提升性能。
五、全链路执行顺序(总览,序列化完整流程)
按 Jackson 真实执行先后排序,一目了然:
- BeanSerializerModifier 执行(修改字段列表、替换Bean级序列化器)
- 扫描字段注解,发现
@JsonSerialize - 若解析器实现
ContextualSerializer→ 执行createContextual构建上下文实例并缓存 - 调用解析器
serialize()方法,输出 JSON
反序列化链路完全对称:
- BeanDeserializerModifier
- 扫描
@JsonDeserialize - 若实现 ContextualDeserializer → 执行 createContextual
- 执行 deserialize()
六、四类组件生效方式汇总表
| 组件 | 生效方式 | 触发阶段 | 作用范围 | 核心特点 |
|---|---|---|---|---|
| BeanSerializerModifier BeanDeserializerModifier |
仅 SimpleModule 注册,无注解 |
Bean解析器构建阶段(一次+缓存) | 全局所有 Java Bean | 增删改字段、替换整个Bean解析器 |
| JsonSerializer JsonDeserializer |
1. @JsonSerialize/@JsonDeserialize 注解2. Module 全局类型绑定 |
字段值读写阶段 | 单个字段 / 整个Java类型 | 纯值转换,不能修改字段结构 |
| ContextualSerializer ContextualDeserializer |
优先注解绑定,也可Module全局绑定 | 1. 构建阶段:createContextual 2. 运行阶段:serialize/deserialize |
单个字段 | 可读取字段注解参数,动态配置逻辑 |
七、常见补充问题
-
Modifier 能控制 ContextualSerializer 吗?
可以。Modifier 可以替换字段对应的BeanPropertyWriter,间接替换字段绑定的解析器。 -
Contextual 解析器可以脱离注解使用吗?
可以,但失去上下文意义,退化为普通 JsonSerializer。 -
全局 Modifier 和局部注解冲突时以谁为准?
Modifier 先执行(改字段结构),注解解析器后执行(改字段值),二者不冲突,各司其职。 -
多次注册 Module,多个 Modifier 会叠加吗?
会,按 Module 注册顺序依次执行多个 Modifier。
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/19942153

浙公网安备 33010602011771号