Spring MVC全局替换Jackson StringDeserializer失效问题排查
我正在开发Spring MVC项目,需要全局替换Jackson默认的StringDeserializer,对传入的JSON字符串做清理处理。目标是无需给200多个DTO的每个字段添加注解,自动对所有DTO的String字段应用清理逻辑。
我已编写自定义StringDeserializer并在Spring配置中注册,但反序列化方法从未被调用。
自定义反序列化器代码
import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StringDeserializer; public class SanitizeDeserializer extends StringDeserializer { @Override public String deserialize( final JsonParser aParser, final DeserializationContext aContext) throws IOException { // 这段代码从未执行 return Jsoup.clean(super.deserialize(aParser, aContext), Safelist.none()); } }
注册反序列化器的Spring配置
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; @Configuration public class SanitizeConfiguration { @Bean public Module addSanitizeStringDeserializer() { // 这段代码启动时能正常执行 final SimpleModule module = new SimpleModule(); module.addDeserializer(String.class, new SanitizeDeserializer()); return module; } }
REST控制器示例
@PutMapping("/document/{id}") public ResponseEntity<String> save(@RequestBody DocumentDTO document) { return ResponseEntity.ok(document.getId()); } @Getters @Setters public class DocumentDTO { // 希望反序列化JSON时,这些String字段在调用setter前先被清理 private String id; private String title; private String content; }
我在deserialize方法中设置了断点,但从未触发,反而进入Jackson原生的StringDeserializer方法。请问是配置错误或遗漏了什么?或者是否无法对已定义类型自定义序列化器?
编辑1:
我尝试配置MessageConverters,但覆盖了默认配置(WebMvcConfigurationSupport.addDefaultHttpMessageConverters),虽然所有String字段都能通过自定义反序列化器处理,但XML内容转换出现异常。
@Override public void configureMessageConverters( final List<HttpMessageConverter<?>> aConverters) { final Jackson2ObjectMapperBuilder iBuilder = new Jackson2ObjectMapperBuilder().deserializerByType(String.class, new SanitizeDeserializer()); aConverters.add(new MappingJackson2HttpMessageConverter(iBuilder.build())); // 删掉下面这行的话,XML响应无法处理(500错误) // 但加上这行,XML响应会被转义,变成无效格式 aConverters.add(new MappingJackson2XmlHttpMessageConverter(new Jackson2ObjectMapperBuilder().xml().build())); }
随后我尝试添加Jackson2ObjectMapperBuilder Bean,但仍然未生效:
@Bean public Jackson2ObjectMapperBuilder configureJackson2ObjectMapperBuilder() { final Jackson2ObjectMapperBuilder iBuilder = new Jackson2ObjectMapperBuilder(); final SimpleModule iSanitizeModule = new SimpleModule("sanitize-module"); iSanitizeModule.addDeserializer(String.class, new SanitizeDeserializer()); iBuilder.modules(iSanitizeModule); return iBuilder; } @Bean public MappingJackson2HttpMessageConverter configureMappingJackson2HttpMessageConverter() { final MappingJackson2HttpMessageConverter iConverter = new MappingJackson2HttpMessageConverter(); iConverter.setObjectMapper(configureJackson2ObjectMapperBuilder().build()); return iConverter; }
问题核心在于Spring MVC中Jackson组件的加载优先级,以及自定义配置与默认配置的冲突。以下是两种可行的解决方式:
方式一:标记自定义ObjectMapper为@Primary
当Spring容器中存在多个ObjectMapper实例时,需标记自定义实例为@Primary,确保Spring MVC优先使用它处理JSON序列化/反序列化,且不会影响XML消息转换器(XML使用独立的XmlMapper实例)。
修改配置类如下:
@Configuration public class SanitizeConfiguration { @Bean @Primary public ObjectMapper sanitizedObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(String.class, new SanitizeDeserializer()); objectMapper.registerModule(module); return objectMapper; } }
方式二:扩展WebMvcConfigurer修改现有转换器
避免继承WebMvcConfigurationSupport(会覆盖默认配置),转而实现WebMvcConfigurer,通过extendMessageConverters方法修改已有的JSON转换器,保留所有默认消息转换器(包括XML):
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // 遍历找到JSON消息转换器,添加自定义反序列化器 for (HttpMessageConverter<?> converter : converters) { if (converter instanceof MappingJackson2HttpMessageConverter) { MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter; SimpleModule module = new SimpleModule(); module.addDeserializer(String.class, new SanitizeDeserializer()); jacksonConverter.getObjectMapper().registerModule(module); break; } } } }
之前配置失效的原因
- 直接返回
ModuleBean:Spring会注册模块,但不会自动将其绑定到所有ObjectMapper实例,默认实例可能未加载该模块。 - 自定义
Jackson2ObjectMapperBuilder未标记@Primary:Spring可能仍使用默认Builder,导致配置不生效。 - 使用
configureMessageConverters替换所有转换器:丢失默认XML转换器的配置,手动添加的XML转换器未继承默认设置,导致格式异常。
内容的提问来源于stack exchange,提问作者kev

