基於挑戰心態,還是實作了某種解法:
- 首先建立一個自己的 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 拿來塗改罷了,有興趣的人可以拿來比較看看,其實上面這個版本比較簡單;並突破了上層類別的限制。
沒有留言 :
張貼留言