Spring Boot升级2.7.0后自定义Jackson Module序列化器被忽略
问题根因
该问题由Spring Boot 2.3+版本对Jackson自动配置的两处默认逻辑调整导致,和2.2RELEASE版本行为存在兼容性差异:
- Module注册顺序变更:2.2版本中用户通过
@Bean定义的Jackson Module会优先于框架内置模块加载,注册的序列化器匹配优先级更高;2.7版本中框架内置Jackson模块、Spring MVC预置的类型序列化器会先于普通用户Module注册,你绑定在Object.class上的通用序列化器会被前置的具体类型匹配规则拦截。 - 序列化匹配规则变更:Spring Boot 2.4+内置Jackson版本升级到2.11+,默认启用最具体类型优先匹配策略:序列化目标对象时,会优先查找该对象实际类型对应的序列化器,查找失败才会沿继承链向上匹配父类序列化器。同时Spring MVC 2.5+会为所有REST接口返回值类型提前注册默认Bean序列化器,匹配流程根本不会回溯到
Object.class层级,因此你的自定义序列化器虽然随Module成功加载,但永远不会被触发。
你断点观察到自定义Module存在于模块列表中是正常现象,问题本质是序列化器的匹配优先级不够,不是Module没有被加载。
修复方案
按稳定性从高到低可选以下方案:
方案1:通过BeanSerializerModifier实现通用序列化拦截(推荐)
不要直接给Object.class注册序列化器,改用Jackson提供的序列化修改器扩展点,该扩展点不受版本匹配规则影响,兼容性最好:
@Configuration public class JacksonConfig { @Bean public Module jacksonModule() { SimpleModule module = new SimpleModule(); module.setSerializerModifier(new BeanSerializerModifier() { @Override public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> defaultSerializer) { Class<?> beanClass = beanDesc.getBeanClass(); // 过滤不需要包装根节点的基础类型、框架内置类型 if (shouldWrapRoot(beanClass)) { // 传入原默认序列化器,避免递归序列化死循环 return new MyCustomSerializer(defaultSerializer); } return defaultSerializer; } }); return module; } private boolean shouldWrapRoot(Class<?> clazz) { return !clazz.isPrimitive() && !Number.class.isAssignableFrom(clazz) && !CharSequence.class.isAssignableFrom(clazz) && !Boolean.class.isAssignableFrom(clazz) && !clazz.getPackage().getName().startsWith("org.springframework.") && !clazz.getPackage().getName().startsWith("com.fasterxml."); } }
方案2:直接使用Jackson内置的根节点包装能力
你的需求是给JSON响应加根节点,不需要自定义序列化器,直接开启框架自带配置即可,稳定性最高:
- 在application.yml中添加配置:
spring: jackson: serialization: # 全局开启根节点包装 wrap-root-value: true
- 对需要自定义根节点名称的返回类,添加
@JsonRootName("自定义根节点名")注解即可,未加注解的类会默认使用类名首字母小写作为根节点名。
方案3:强制提升自定义Module的注册优先级
如果要完全保留原有直接注册Object.class序列化器的写法,不要直接注册Module Bean,改用Jackson2ObjectMapperBuilderCustomizer手动注入模块并设置最高优先级:
@Configuration public class JacksonConfig { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() { return builder -> { SimpleModule module = new SimpleModule(); module.addSerializer(Object.class, new MyCustomSerializer()); // 指定模块优先安装,优先级高于内置模块 builder.modulesToInstall(module); }; } }
内容的提问来源于stack exchange,提问作者Shvalb
相关产品推荐
相关产品推荐

