自定义Jackson HttpMessageConverter不再适用于Spring 4.2

我正在将一个应用程序从Spring Platform 1.1.3.RELEASE更新到2.0.1.RELEASE,它将Spring Framework版本从4.1.7升级到4.2.4,将Jackson从2.4.6升级到2.6.4。 Spring或Jackson处理自定义HttpMessageConverter实现似乎没有任何重大变化,但我的自定义JSON序列化未能发生,我无法确定原因。 以下在Spring Platform版本中可以正常工作:

模型

 @JsonFilter("fieldFilter") public class MyModel { /*model fields and methods*/ } 

模型包装

 public class ResponseEnvelope { private Set fieldSet; private Set exclude; private Object entity; public ResponseEnvelope(Object entity) { this.entity = entity; } public ResponseEnvelope(Object entity, Set fieldSet, Set exclude) { this.fieldSet = fieldSet; this.exclude = exclude; this.entity = entity; } public Object getEntity() { return entity; } @JsonIgnore public Set getFieldSet() { return fieldSet; } @JsonIgnore public Set getExclude() { return exclude; } public void setExclude(Set exclude) { this.exclude = exclude; } public void setFieldSet(Set fieldSet) { this.fieldSet = fieldSet; } public void setFields(String fields) { Set fieldSet = new HashSet(); if (fields != null) { for (String field : fields.split(",")) { fieldSet.add(field); } } this.fieldSet = fieldSet; } } 

调节器

 @Controller public class MyModelController { @Autowired MyModelRepository myModelRepository; @RequestMapping(value = "/model", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE }) public HttpEntity find(@RequestParam(required=false) Set fields, @RequestParam(required=false) Set exclude){ List objects = myModelRepository.findAll(); ResponseEnvelope envelope = new ResponseEnvelope(objects, fields, exclude); return new ResponseEntity(envelope, HttpStatus.OK); } } 

自定义HttpMessageConverter

 public class FilteringJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { private boolean prefixJson = false; @Override public void setPrefixJson(boolean prefixJson) { this.prefixJson = prefixJson; super.setPrefixJson(prefixJson); } @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { ObjectMapper objectMapper = getObjectMapper(); JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputMessage.getBody()); try { if (this.prefixJson) { jsonGenerator.writeRaw(")]}', "); } if (object instanceof ResponseEnvelope) { ResponseEnvelope envelope = (ResponseEnvelope) object; Object entity = envelope.getEntity(); Set fieldSet = envelope.getFieldSet(); Set exclude = envelope.getExclude(); FilterProvider filters = null; if (fieldSet != null && !fieldSet.isEmpty()) { filters = new SimpleFilterProvider() .addFilter("fieldFilter", SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet)) .setFailOnUnknownId(false); } else if (exclude != null && !exclude.isEmpty()) { filters = new SimpleFilterProvider() .addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept(exclude)) .setFailOnUnknownId(false); } else { filters = new SimpleFilterProvider() .addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept()) .setFailOnUnknownId(false); } objectMapper.setFilterProvider(filters); objectMapper.writeValue(jsonGenerator, entity); } else if (object == null){ jsonGenerator.writeNull(); } else { FilterProvider filters = new SimpleFilterProvider().setFailOnUnknownId(false); objectMapper.setFilterProvider(filters); objectMapper.writeValue(jsonGenerator, object); } } catch (JsonProcessingException e){ e.printStackTrace(); throw new HttpMessageNotWritableException("Could not write JSON: " + e.getMessage()); } } } 

组态

 @Configuration @EnableWebMvc public class WebServicesConfig extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter> converters) { FilteringJackson2HttpMessageConverter jsonConverter = new FilteringJackson2HttpMessageConverter(); jsonConverter.setSupportedMediaTypes(MediaTypes.APPLICATION_JSON); converters.add(jsonConverter); } // Other configurations } 

现在我收到此exception(由Spring捕获并记录)并在发出任何类型的请求时出现500错误:

 [main] WARN oswsmsDefaultHandlerExceptionResolver - Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Can not resolve PropertyFilter with id 'fieldFilter'; no FilterProvider configured (through reference chain: org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not resolve PropertyFilter with id 'fieldFilter'; no FilterProvider configured (through reference chain: org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]->java.util.ArrayList[0]) 

configureMessageConverters方法执行,但它看起来不像在请求期间使用自定义转换器。 是否有可能另一个消息转换器阻止此消息转换到我的响应? 我的理解是,覆盖configureMessageConverters会阻止使用除手动注册的转换器以外的转换器。

除了通过Spring Platform更新依赖版本之外,此代码的工作版本和非工作版本之间没有任何更改。 我在文档中遗漏的JSON序列化是否有任何变化?

编辑

进一步测试产生奇怪的结果 我想测试以检查以下内容:

  1. 我的自定义HttpMessageConverter实际上是否已注册?
  2. 另一个转换器是否覆盖/取代它?
  3. 这只是我的测试设置的问题吗?

所以,我添加了一个额外的测试并看了一下输出:

 @Autowired WebApplicationContext webApplicationContext; @Before public void setup(){ mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } @Test public void test() throws Exception { RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) webApplicationContext.getBean("requestMappingHandlerAdapter"); List genes = EntrezGene.createDummyData(); Set exclude = new HashSet(); exclude.add("entrezGeneId"); ResponseEnvelope envelope = new ResponseEnvelope(genes, new HashSet(), exclude); for (HttpMessageConverter converter: adapter.getMessageConverters()){ System.out.println(converter.getClass().getName()); if (converter.canWrite(ResponseEnvelope.class, MediaType.APPLICATION_JSON)){ MockHttpOutputMessage message = new MockHttpOutputMessage(); converter.write((Object) envelope, MediaType.APPLICATION_JSON, message); System.out.println(message.getBodyAsString()); } } } 

……它工作正常。 我的envelope对象及其内容被序列化并正确过滤。 因此,请求处理在到达消息转换器之前存在问题,或者MockMvc测试请求的方式发生了变化。

你的配置还可以。 不从自定义转换器调用writeInternal()的原因是因为您覆盖了错误的方法。

查看4.2.4.RELEASE的源代码

AbstractMessageConverterMethodProcessor#writeWithMessageConverters

 protected  void writeWithMessageConverters(T returnValue, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { ... ((GenericHttpMessageConverter) messageConverter).write(returnValue, returnValueType, selectedMediaType, outputMessage); ... } 

AbstractGenericHttpMessageConverter#写

 public final void write(final T t, final Type type, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { ... writeInternal(t, type, outputMessage); ... } 

AbstractGenericHttpMessageConverter#write(...) writeInternal(...)调用的writeInternal(...)方法有三个参数 – (T t, Type type, HttpOutputMessage outputMessage) 。 您正在覆盖只有2个参数的writeInternal(...)的重载版本 – (T t, HttpOutputMessage outputMessage)

但是,在4.1.7.RELEASE版本中,情况并非如此,因此是问题的根本原因。 此版本中使用的writeInternal(...)是您重写的另一个重载方法(具有2个参数的方法)。 这解释了为什么它在4.1.7.RELEASE正常工作。

 @Override public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { ... writeInternal(t, outputMessage); ... } 

因此,要解决您的问题,而不是重写writeInternal(Object object, HttpOutputMessage outputMessage) ,重写writeInternal(Object object, Type type, HttpOutputMessage outputMessage)