当前位置 博文首页 > 韩超的博客 (hanchao5272):spring-mvc引入jackson-dataformat-x
工程是一个spring-mvc
,使用jackson
作为RestController
序列化组件,一切都相安无事。
直到有一天添加依赖的时候,间接引入了jackson-dataformat-xml
,惊奇的发现部分接口的返回竟然从json
变成了xml
….
带着满脸奔跑的草泥马,不禁提出了3个问题:
既然突然变成了xml,我又没有修改spring序列化相关配置,那么肯定和spring的自动发现有关系了。
打开pom
准备看看有没有新增的xml
相关的组件,果然有一个jackson-dataformat-xml
。
继续探寻问题的本源…
统计了一下异常接口和正常接口的规律,发现正常接口都是以.json结尾,异常接口没有任何后缀,那么6,给个后缀spring就知道我要返回json?
那没理由不给后缀猜不到啊!跟了一下spring的源码,发现spring确实尽力了…
打开org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
,让我们来看第180
行至第210
行。
版本说明:spring-webmvc-4.3.22.RELEASE.jar
/**
* Writes the given return type to the given output message.
* @param value the value to write to the output message
* @param returnType the type of the value
* @param inputMessage the input messages. Used to inspect the {@code Accept} header.
* @param outputMessage the output message to write to
* @throws IOException thrown in case of I/O errors
* @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated
* by the {@code Accept} header on the request cannot be met by the message converters
*/
@SuppressWarnings("unchecked")
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//...
//Line 183 : 从HttpServletRequest中获取媒体类型
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
//Line 184 : 从request mappings中获取媒体类型
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
if (outputValue != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
}
//Line 189 : 遍历两个媒体类型集合,构成 兼容的媒体类型
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
for (MediaType requestedType : requestedMediaTypes) {
for (MediaType producibleType : producibleMediaTypes) {
//Line 193 : 兼容判断
if (requestedType.isCompatibleWith(producibleType)) {
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (compatibleMediaTypes.isEmpty()) {
if (outputValue != null) {
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
}
return;
}
List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
//Line 206 : 根据MediaType的特异性(主要标准)和质量(次要标准)进行排序
MediaType.sortBySpecificityAndQuality(mediaTypes);
MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
为什么后缀带.json的接口的没问题呢?
因为第183
行,由接口后缀推断出了请求的requestedMediaTypes
是application/json
,而没有任何后缀,只能推断出来都接受。
那为什么加上依赖之后就都有问题了呢?
因为之前第193
行并不包括application/xml
,所以无论什么接口,能返回json
的都返回json
。
加上依赖之后,在第206
行排序过程中,虽然application/json
和application/xml
是相等的,但由于这个排序是稳定排序(相等值顺序不变),而且application/xml
的记载顺序更先,因此总是选中了application/xml
。
方式一
指定一个默认配置,比如这样
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="defaultContentType" value="application/json" />
</bean>
方式二
如果不需要这个包,可以直接通过排除依赖(exclusion
)的方式避免此问题。
原文链接:https://blog.csdn.net/liang16286/article/details/80091466
cs