Refactor @JsonView support w/ ResponseBodyInterceptor

The newly added support for ResponseBodyInterceptor is a good fit for
the (also recently added) support for the Jackson @JsonView annotation.

This change refactors the original implementation of @JsonView support
for @ResponseBody and ResponseEntity controller methods this time
implemented as an ResponseBodyInterceptor.

Issue: SPR-7156
This commit is contained in:
Rossen Stoyanchev
2014-05-18 19:20:58 -04:00
parent 96b18c8dc2
commit 51fc3b4aaf
17 changed files with 321 additions and 134 deletions

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http.converter;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import java.io.IOException;
/**
* An HttpMessageConverter that supports converting the value returned from a
* method by incorporating {@link org.springframework.core.MethodParameter}
* information into the conversion. Such a converter can for example take into
* account information from method-level annotations.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
public interface MethodParameterHttpMessageConverter<T> extends HttpMessageConverter<T> {
/**
* This method mirrors {@link HttpMessageConverter#canRead(Class, MediaType)}
* with an additional {@code MethodParameter}.
*/
boolean canRead(Class<?> clazz, MediaType mediaType, MethodParameter parameter);
/**
* This method mirrors {@link HttpMessageConverter#canWrite(Class, MediaType)}
* with an additional {@code MethodParameter}.
*/
boolean canWrite(Class<?> clazz, MediaType mediaType, MethodParameter parameter);
/**
* This method mirrors {@link HttpMessageConverter#read(Class, HttpInputMessage)}
* with an additional {@code MethodParameter}.
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage, MethodParameter parameter)
throws IOException, HttpMessageNotReadableException;
/**
* This method mirrors {@link HttpMessageConverter#write(Object, MediaType, HttpOutputMessage)}
* with an additional {@code MethodParameter}.
*/
void write(T t, MediaType contentType, HttpOutputMessage outputMessage, MethodParameter parameter)
throws IOException, HttpMessageNotWritableException;
}

View File

@@ -21,7 +21,6 @@ import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -30,7 +29,6 @@ import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
@@ -38,7 +36,6 @@ import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.MethodParameterHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -60,7 +57,7 @@ import org.springframework.util.ClassUtils;
* @since 3.1.2
*/
public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConverter<Object>
implements GenericHttpMessageConverter<Object>, MethodParameterHttpMessageConverter<Object> {
implements GenericHttpMessageConverter<Object> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
@@ -150,11 +147,6 @@ public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConv
}
}
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType, MethodParameter parameter) {
return canRead(clazz, mediaType);
}
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return canRead(clazz, null, mediaType);
@@ -205,11 +197,6 @@ public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConv
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType, MethodParameter parameter) {
return canWrite(clazz, mediaType);
}
@Override
protected boolean supports(Class<?> clazz) {
// should not be called, since we override canRead/Write instead
@@ -224,11 +211,6 @@ public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConv
return readJavaType(javaType, inputMessage);
}
@Override
public Object read(Class<?> clazz, HttpInputMessage inputMessage, MethodParameter parameter) throws IOException, HttpMessageNotReadableException {
return super.read(clazz, inputMessage);
}
@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
@@ -267,8 +249,8 @@ public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConv
if (this.jsonPrefix != null) {
jsonGenerator.writeRaw(this.jsonPrefix);
}
if (object instanceof MappingJacksonValueHolder) {
MappingJacksonValueHolder valueHolder = (MappingJacksonValueHolder) object;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue valueHolder = (MappingJacksonValue) object;
object = valueHolder.getValue();
Class<?> serializationView = valueHolder.getSerializationView();
this.objectMapper.writerWithView(serializationView).writeValue(jsonGenerator, object);
@@ -282,20 +264,6 @@ public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConv
}
}
@Override
public void write(Object object, MediaType contentType, HttpOutputMessage outputMessage, MethodParameter parameter)
throws IOException, HttpMessageNotWritableException {
JsonView annot = parameter.getMethodAnnotation(JsonView.class);
if (annot != null && annot.value().length != 0) {
MappingJacksonValueHolder serializationValue = new MappingJacksonValueHolder(object, annot.value()[0]);
super.write(serializationValue, contentType, outputMessage);
}
else {
super.write(object, contentType, outputMessage);
}
}
/**
* Return the Jackson {@link JavaType} for the specified type and context class.
* <p>The default implementation returns {@code typeFactory.constructType(type, contextClass)},
@@ -339,16 +307,16 @@ public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConv
@Override
protected MediaType getDefaultContentType(Object object) throws IOException {
if (object instanceof MappingJacksonValueHolder) {
object = ((MappingJacksonValueHolder) object).getValue();
if (object instanceof MappingJacksonValue) {
object = ((MappingJacksonValue) object).getValue();
}
return super.getDefaultContentType(object);
}
@Override
protected Long getContentLength(Object object, MediaType contentType) throws IOException {
if (object instanceof MappingJacksonValueHolder) {
object = ((MappingJacksonValueHolder) object).getValue();
if (object instanceof MappingJacksonValue) {
object = ((MappingJacksonValue) object).getValue();
}
return super.getContentLength(object, contentType);
}

View File

@@ -25,7 +25,7 @@ package org.springframework.http.converter.json;
*
* @see com.fasterxml.jackson.annotation.JsonView
*/
public class MappingJacksonValueHolder {
public class MappingJacksonValue {
private final Object value;
@@ -37,7 +37,7 @@ public class MappingJacksonValueHolder {
* @param value the Object to be serialized
* @param serializationView the view to be applied
*/
public MappingJacksonValueHolder(Object value, Class<?> serializationView) {
public MappingJacksonValue(Object value, Class<?> serializationView) {
this.value = value;
this.serializationView = serializationView;
}