序列化 之 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);
}

执行流程:

  1. Jackson 初始化序列化器
  2. 调用 createContextual,传入当前字段 BeanProperty
  3. 从 BeanProperty 获取字段注解、字段名、所属类
  4. 根据注解参数,动态new 自定义序列化器实例
  5. 最终使用返回的序列化器执行 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:指定序列化器生效

两种方式:

  1. 字段直接指定
@JsonSerialize(using = SensitiveSerializer.class)
@Sensitive(type = SensitiveType.PHONE)
private String phone;
  1. 全局注册(统一拦截 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);
    }
}

缺点:全局统一逻辑,不能针对单个字段差异化


五、核心底层执行流程(必看)

序列化流程

  1. Spring MVC 响应结果 → Jackson 消息转换器
  2. 查找字段上 @JsonSerialize 指定的序列化器
  3. 判断是否实现 ContextualSerializer
    • 是 → 执行 createContextual,读取注解 → 生成定制序列化器
    • 否 → 直接使用原始序列化器
  4. 调用 serialize 方法输出 JSON

反序列化流程

  1. 前端 JSON 入参 → Jackson 解析
  2. 查找 @JsonDeserialize 指定反序列化器
  3. 实现 ContextualDeserializer → 执行 createContextual
  4. 调用 deserialize 完成解析

六、关键要点总结

  1. 单纯 JsonSerializer/Deserializer

    • 无上下文、无注解读取能力
    • 适合:全局统一格式化、简单转换
  2. Contextual 上下文接口

    • 核心方法:createContextual
    • 核心对象:BeanProperty(获取字段注解、字段类型、类信息)
    • 适合:脱敏、加解密、动态日期格式、枚举自定义解析、字典翻译
  3. SpringBoot 整合

    • 配合 @JsonSerialize / @JsonDeserialize 注解使用
    • 或通过 SimpleModule 全局注册,批量处理类型
  4. 生产常用组合

    • 脱敏、数据加解密、数据翻译:必用 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. 典型使用场景

  1. 全局统一屏蔽敏感字段(密码、手机号、身份证)
  2. 动态根据环境/权限显示/隐藏字段
  3. 批量修改一批类的字段名、字段顺序
  4. 统一给某类 Bean 追加额外 JSON 字段
  5. 包装默认 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. 典型场景

  1. 全局忽略前端传过来的多余脏字段
  2. 动态调整 JSON → Java 字段映射关系
  3. 统一对一类 Bean 做反序列化前置校验、数据清洗
  4. 替换整个 Bean 的反序列化逻辑

四、JsonSerializer / JsonDeserializer 详解(字段/类型级序列化器)

1. 类定位

  • 序列化:com.fasterxml.jackson.databind.JsonSerializer<T>
  • 反序列化:com.fasterxml.jackson.databind.JsonDeserializer<T>

最小执行单元:只负责单个类型 / 单个字段的序列化/反序列化逻辑。
使用方式两种:

  1. 注解方式:直接加在字段/类上(局部生效)
  2. 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. 典型场景

  1. 单个字段特殊格式化(日期、枚举、金额、加密字段)
  2. 字段值转换(Long → String、脱敏、截断)
  3. 枚举自定义输出文本
  4. 一对一、精准控制某一个类型/某一个字段的解析规则

五、核心区别对比(重点)

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. 关键补充误区

  1. Modifier 不能替代 JsonSerializer
    Modifier 是“包工头”,负责安排有哪些字段;JsonSerializer 是“工人”,负责把字段值写成 JSON。

  2. JsonSerializer 无法增减字段
    它只能处理当前字段的值,不能决定这个字段要不要出现在 JSON 里。

  3. 修饰器是全局 Module 扩展,一旦注册对所有匹配的 Bean生效;
    注解式 JsonSerializer 只对当前字段/当前类生效,局部优先级更高。


六、一句话总结

  • Modifier:管整个 Bean 的字段列表、解析器整体,做流程和结构改造;
  • JsonSerializer/Deserializer:管单个字段/类型的值转换,做数据内容改造。
    两者常搭配使用,各司其职。

三 执行顺序

结合 Jackson 执行流程、注册方式、注解规则、优先级,分三类讲清生效条件、触发时机、执行链路、优先级,附带示例与使用边界。

一、整体前置

四类组件归属:

  1. BeanSerializerModifier / BeanDeserializerModifier:Bean 构建阶段修改器(全局 Module 扩展)
  2. JsonSerializer / JsonDeserializer:基础序列化/反序列化器(字段/类型级别)
  3. ContextualSerializer / ContextualDeserializer:带上下文感知的序列化/反序列化器(继承自上面基础接口,支持读取注解属性)

生效核心规则:先执行 Modifier(改结构/改解析器)→ 再执行 普通/上下文 Serializer(处理字段值)


二、BeanSerializerModifier / BeanDeserializerModifier 生效机制

1. 核心前提

必须注册到 SimpleModule,再注册进 ObjectMapper,无注解用法,全局生效。
作用阶段:Jackson 创建 BeanSerializer / BeanDeserializer 实例的构建阶段。

2. 生效步骤(序列化侧)

  1. 初始化 SimpleModule,调用 setSerializerModifier() 绑定自定义 Modifier
  2. module 注册到 ObjectMapper
  3. 当 ObjectMapper 第一次序列化某个 Java Bean 时:
    • Jackson 扫描 Bean 所有属性、注解
    • 触发 BeanSerializerModifier 生命周期方法:modifyProperties → changeProperties → modifySerializer
    • 动态增删字段、重命名、替换整个 Bean 序列化器
  4. 构建完成后缓存 BeanSerializer,后续同类型 Bean 直接走缓存,不再重复执行 Modifier

3. 反序列化侧(对称逻辑)

  1. 同样通过 SimpleModule.setDeserializerModifier() 注册
  2. 第一次反序列化某 Bean 时触发:modifyProperties → modifyDeserializer
  3. 调整可反序列化字段、替换整体反序列化器,结果缓存

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;
}

生效逻辑

  1. Jackson 构建 Bean 属性时,扫描字段/类上的 @JsonSerialize/@JsonDeserialize
  2. 为当前属性单独绑定指定的 Serializer/Deserializer
  3. 序列化/反序列化运行阶段,直接调用该实例执行逻辑
  4. 范围:仅当前标注的字段/类生效,其他同类型字段不受影响

方式2:Module 全局绑定(类型级别)

对整个 Java 类型统一绑定解析器,全局所有该类型字段都生效。

SimpleModule module = new SimpleModule();
// 全局:所有 String 类型都使用该序列化器
module.addSerializer(String.class, new MyStrSerializer());
// 全局:所有 Date 类型都使用该反序列化器
module.addDeserializer(Date.class, new MyDateDeserializer());
mapper.registerModule(module);

生效逻辑

  1. Module 注册后,ObjectMapper 维护「Java类型 → 解析器」映射表
  2. 遇到对应类型字段,自动匹配全局绑定的解析器
  3. 范围:项目内所有该类型字段

优先级规则(重要)

注解局部解析器 > 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 构建阶段,只执行一次 + 缓存)

  1. 字段标注 @JsonSerialize(using = XxxContextualSerializer.class)
  2. Jackson 扫描到该解析器实现了 ContextualSerializer
  3. 调用 createContextual():
    • 通过 BeanProperty 获取当前字段上的自定义注解、注解参数
    • 根据参数创建/配置真正干活的 Serializer 实例并缓存
  4. 此阶段和 Modifier 同属「解析器构建期」

阶段2:实际序列化/反序列化(运行阶段)

  1. 复用上一步缓存好的、带上下文配置的解析器实例
  2. 调用父接口 serialize() / deserialize() 执行业务逻辑
  3. 不会重复执行 createContextual(同字段全局缓存)

3. 生效方式

和普通 JsonSerializer 完全一致,支持两种用法:

  1. 注解绑定(主流):@JsonSerialize(using = 实现类.class) 标注在字段/类上
  2. 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. 优先级 & 特殊规则

  1. 优先级:和普通 JsonSerializer 一致:注解 > 全局Module绑定
  2. 执行顺序:
    BeanSerializerModifier → createContextual(上下文初始化) → serialize(实际序列化)
  3. 缓存特性:
    每个字段对应一个独立上下文实例,同字段复用缓存,提升性能。

五、全链路执行顺序(总览,序列化完整流程)

按 Jackson 真实执行先后排序,一目了然:

  1. BeanSerializerModifier 执行(修改字段列表、替换Bean级序列化器)
  2. 扫描字段注解,发现 @JsonSerialize
  3. 若解析器实现 ContextualSerializer → 执行 createContextual 构建上下文实例并缓存
  4. 调用解析器 serialize() 方法,输出 JSON

反序列化链路完全对称:

  1. BeanDeserializerModifier
  2. 扫描 @JsonDeserialize
  3. 若实现 ContextualDeserializer → 执行 createContextual
  4. 执行 deserialize()

六、四类组件生效方式汇总表

组件 生效方式 触发阶段 作用范围 核心特点
BeanSerializerModifier
BeanDeserializerModifier
仅 SimpleModule 注册,无注解 Bean解析器构建阶段(一次+缓存) 全局所有 Java Bean 增删改字段、替换整个Bean解析器
JsonSerializer
JsonDeserializer
1. @JsonSerialize/@JsonDeserialize 注解
2. Module 全局类型绑定
字段值读写阶段 单个字段 / 整个Java类型 纯值转换,不能修改字段结构
ContextualSerializer
ContextualDeserializer
优先注解绑定,也可Module全局绑定 1. 构建阶段:createContextual
2. 运行阶段:serialize/deserialize
单个字段 可读取字段注解参数,动态配置逻辑

七、常见补充问题

  1. Modifier 能控制 ContextualSerializer 吗?
    可以。Modifier 可以替换字段对应的 BeanPropertyWriter,间接替换字段绑定的解析器。

  2. Contextual 解析器可以脱离注解使用吗?
    可以,但失去上下文意义,退化为普通 JsonSerializer。

  3. 全局 Modifier 和局部注解冲突时以谁为准?
    Modifier 先执行(改字段结构),注解解析器后执行(改字段值),二者不冲突,各司其职。

  4. 多次注册 Module,多个 Modifier 会叠加吗?
    会,按 Module 注册顺序依次执行多个 Modifier。

posted @ 2026-04-28 10:28  蓝迷梦  阅读(51)  评论(0)    收藏  举报