Java 自定义Jackson HttpMessageConverter在Spring 4.2中不再工作
我正在将一个应用程序从Spring平台版本1.1.3.RELEASE升级到2.0.1.RELEASE,这将Spring框架版本从4.1.7升级到4.2.4,将Jackson从2.4.6升级到2.6.4。Spring或Jackson对自定义Java 自定义Jackson HttpMessageConverter在Spring 4.2中不再工作,java,json,spring-mvc,jackson,Java,Json,Spring Mvc,Jackson,我正在将一个应用程序从Spring平台版本1.1.3.RELEASE升级到2.0.1.RELEASE,这将Spring框架版本从4.1.7升级到4.2.4,将Jackson从2.4.6升级到2.6.4。Spring或Jackson对自定义HttpMessageConverter实现的处理似乎没有任何重大变化,但我的自定义JSON序列化失败了,我无法确定原因。在上一个Spring Platform版本中,以下功能可以正常工作: 型号 @JsonFilter("fieldFilter") publi
HttpMessageConverter
实现的处理似乎没有任何重大变化,但我的自定义JSON序列化失败了,我无法确定原因。在上一个Spring Platform版本中,以下功能可以正常工作:
型号
@JsonFilter("fieldFilter")
public class MyModel {
/*model fields and methods*/
}
模型包装器
public class ResponseEnvelope {
private Set<String> fieldSet;
private Set<String> exclude;
private Object entity;
public ResponseEnvelope(Object entity) {
this.entity = entity;
}
public ResponseEnvelope(Object entity, Set<String> fieldSet, Set<String> exclude) {
this.fieldSet = fieldSet;
this.exclude = exclude;
this.entity = entity;
}
public Object getEntity() {
return entity;
}
@JsonIgnore
public Set<String> getFieldSet() {
return fieldSet;
}
@JsonIgnore
public Set<String> getExclude() {
return exclude;
}
public void setExclude(Set<String> exclude) {
this.exclude = exclude;
}
public void setFieldSet(Set<String> fieldSet) {
this.fieldSet = fieldSet;
}
public void setFields(String fields) {
Set<String> fieldSet = new HashSet<String>();
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<String> fields, @RequestParam(required=false) Set<String> exclude){
List<MyModel> objects = myModelRepository.findAll();
ResponseEnvelope envelope = new ResponseEnvelope(objects, fields, exclude);
return new ResponseEntity<>(envelope, HttpStatus.OK);
}
}
配置
@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
}
执行configureMessageConverters
方法,但在请求期间似乎从未使用过自定义转换器。是否有可能是另一个消息转换器阻止此消息到达我的响应?我的理解是,覆盖configureMessageConverters
将阻止使用手动注册的转换器以外的转换器
除了通过Spring平台更新依赖项版本外,此代码的工作版本和非工作版本之间没有进行任何更改。JSON序列化中是否有我在文档中遗漏的任何更改
编辑
进一步的测试产生了奇怪的结果。我想测试以检查以下内容:
HttpMessageConverter
是否已实际注册@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<EntrezGene> genes = EntrezGene.createDummyData();
Set<String> exclude = new HashSet<>();
exclude.add("entrezGeneId");
ResponseEnvelope envelope = new ResponseEnvelope(genes, new HashSet<String>(), 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());
}
}
}
@Autowired-WebApplicationContext-WebApplicationContext;
@以前
公共作废设置(){
mockMvc=MockMvcBuilders.webAppContextSetup(webApplicationContext.build();
}
@试验
public void test()引发异常{
RequestMappingHandlerAdapter=(RequestMappingHandlerAdapter)webApplicationContext.getBean(“RequestMappingHandlerAdapter”);
List genes=EntrezGene.createDummyData();
Set exclude=new HashSet();
排除。添加(“entrezGeneId”);
ResponseEnvelope=新ResponseEnvelope(基因,新HashSet(),排除);
对于(HttpMessageConverter:adapter.getMessageConverters()){
System.out.println(converter.getClass().getName());
if(converter.canWrite(responseenevelope.class,MediaType.APPLICATION_JSON)){
MockHttpOutputMessage=新的MockHttpOutputMessage();
write((对象)信封,MediaType.APPLICATION_JSON,message);
System.out.println(message.getBodyAsString());
}
}
}
…而且效果很好。我的
信封
对象及其内容已正确序列化和筛选。因此,在请求到达消息转换器之前,它的处理存在问题,或者,MockMvc
测试请求的方式发生了变化。您的配置正常。未从自定义转换器调用writeInternal()
的原因是重写了错误的方法
查看4.2.4.发行版的源代码
抽象消息转换器方法处理器#编写消息转换器
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
((GenericHttpMessageConverter<T>) messageConverter).write(returnValue, returnValueType, selectedMediaType, outputMessage);
...
}
从AbstractGenericHttpMessageConverter#write(…)
中调用的方法writeInternal(…)
有三个参数-(T,Type Type,HttpOutputMessage outputMessage)
。您正在重写重载版本的writeInternal(…)
,该版本只有两个参数-(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(对象对象,HttpOutputMessage outputMessage)
,而要重写writeInternal(对象对象,类型,HttpOutputMessage outputMessage)
在configureMessageConverters
中放置一个断点,并确保它正在执行。@chrylis:已经尝试过了,配置方法正在执行。您能提供完整的stacktrace吗?@frant.hartm:我在日志中添加了完整的错误消息。Spring容器捕获异常并生成一个500错误的响应。@woemler您是否已将方法setFilters(filters)
更改为setFilterProvider(filters)
?以前没有。与setFilters()
不同,setFilterProvider()
返回ObjectMapper
,因此您不应该执行ObjectMapper.setFilterProvider(filters).writeValue(jsonGenerator,object)代码>?成功了!非常感谢你!令人费解的是,为什么他们决定更改此方法的功能,但至少我现在对引擎盖下发生的事情有了更好的了解。诀窍是将Spring logs设置为ALL,以获得有关内部发生情况的精彩故事:)
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
((GenericHttpMessageConverter<T>) messageConverter).write(returnValue, returnValueType, selectedMediaType, outputMessage);
...
}
public final void write(final T t, final Type type, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
...
writeInternal(t, type, outputMessage);
...
}
@Override
public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
...
writeInternal(t, outputMessage);
...
}