基於挑戰心態,還是實作了某種解法:
- 首先建立一個自己的 JacksonHttpMessageConverter 實作。
- 在載入階段更換預載的 MappingJacksonHttpMessageConverter 實例。
- 完成。
以下這段 xml 是在描述 spring 載入 mvc 時的設置:
<mvc:annotation-driven/> <bean class="funweb.spring.JsonHackProcessor"/>
JsonHackProcessor 實作
package funweb.spring;
import java.util.ArrayList;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
public class JsonHackProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AnnotationMethodHandlerAdapter) {
AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
HttpMessageConverter<?>[] converters = adapter.getMessageConverters();
ArrayList<HttpMessageConverter<?>> list = new ArrayList<>();
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
list.add(new JacksonHttpMessageConverter());
continue;
}
list.add(converter);
}
converters = list.toArray(converters);
adapter.setMessageConverters(converters);
}
return bean;
}
}
大致上的做法是透過 AnnotationMethodHandlerAdapter 類別取出預載的 MessageConverter 實例陣列,其中一個會是 MappingJacksonHttpMessageConverter 實例,將它替換成我們的實例即可。
JacksonHttpMessageConverter 實作
package funweb.spring;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.*;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.springframework.http.*;
import org.springframework.http.converter.*;
public class JacksonHttpMessageConverter implements HttpMessageConverter<Object> {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private final MediaType supportedMediaType = new MediaType("application", "json", DEFAULT_CHARSET);
private final List<MediaType> supportedMediaTypes = Collections.unmodifiableList(Collections.singletonList(supportedMediaType));
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
JavaType javaType = getJavaType(clazz);
return this.objectMapper.canDeserialize(javaType) && canRead(mediaType);
}
protected boolean canRead(MediaType mediaType) {
if (mediaType == null) {
return true;
}
return supportedMediaType.includes(mediaType);
}
@SuppressWarnings("deprecation")
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.type(clazz);
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return this.objectMapper.canSerialize(clazz) && canWrite(mediaType);
}
protected boolean canWrite(MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equals(mediaType)) {
return true;
}
return supportedMediaType.isCompatibleWith(mediaType);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return this.supportedMediaTypes;
}
@Override
public Object read(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(clazz);
try {
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
}
catch (JsonParseException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
}
@Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
HttpHeaders headers = outputMessage.getHeaders();
if (headers.getContentType() == null) {
if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
contentType = supportedMediaType;
}
if (contentType != null) {
headers.setContentType(contentType);
}
}
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outStream, encoding);
try {
this.objectMapper.writeValue(jsonGenerator, o);
byte[] buff = outStream.toByteArray();
headers.setContentLength(outStream.size());
IOUtils.copy(new ByteArrayInputStream(buff), outputMessage.getBody());
}
catch (JsonGenerationException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
outputMessage.getBody().flush();
}
private JsonEncoding getEncoding(MediaType contentType) {
if (contentType != null && contentType.getCharSet() != null) {
Charset charset = contentType.getCharSet();
for (JsonEncoding encoding : JsonEncoding.values()) {
if (charset.name().equals(encoding.getJavaName())) {
return encoding;
}
}
}
return JsonEncoding.UTF8;
}
}
可別被上面這段程式碼給嚇著了,其實也不過是將原始的 MappingJacksonHttpMessageConverter.java 拿來塗改罷了,有興趣的人可以拿來比較看看,其實上面這個版本比較簡單;並突破了上層類別的限制。
沒有留言 :
張貼留言