当我们的地址以json和xml结尾时,spring boot会选择对应的转换器来进行转换
比如,输入
http://ip:port/hello.json 结果返回json格式的{"code":100,"msg":"hello"}
http://ip:port/hello.xml 结果返回xml格式的
<xml>
<code>100</code>
<msg>hello</msg>
</xml
首先,系统在初始化的时候,通常会加载json和xml两种格式
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
protected Map<String, MediaType> getDefaultMediaTypes() {
Map<String, MediaType> map = new HashMap<String, MediaType>(4);
if (romePresent) {
map.put("atom", MediaType.APPLICATION_ATOM_XML);
map.put("rss", MediaType.APPLICATION_RSS_XML);
}
if (jaxb2Present || jackson2XmlPresent) {
map.put("xml", MediaType.APPLICATION_XML);
}
if (jackson2Present || gsonPresent) {
map.put("json", MediaType.APPLICATION_JSON);
}
return map;
}
如果DefaultMediaTypes中找不到的话,他就会去MimeMappings中查找
那么其它的类型在哪里呢,org.springframework.boot.context.embedded.MimeMappings
static {
MimeMappings mappings = new MimeMappings();
mappings.add("abs", "audio/x-mpeg");
mappings.add("ai", "application/postscript");
mappings.add("aif", "audio/x-aiff");
mappings.add("aifc", "audio/x-aiff");
.....太多了,不copy了
mappings.add("xhtml", "application/xhtml+xml");
所以这也是为什么xhtml最终可以输出xml格式
在AbstractMessageConverterMethodProcessor中
找到
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object outputValue;
Class<?> valueType;
Type declaredType;
if (value instanceof CharSequence) {
outputValue = value.toString();
valueType = String.class;
declaredType = String.class;
}
else {
outputValue = value;
valueType = getReturnValueType(outputValue, returnType);
declaredType = getGenericType(returnType);
}
HttpServletRequest request = inputMessage.getServletRequest();
//这里从request中获取支MediaType(header或uri末尾)
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
if (outputValue != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
}
....
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
//循环去匹配对应的convert转换器
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
if (((GenericHttpMessageConverter) messageConverter).canWrite(
declaredType, valueType, selectedMediaType)) {
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
inputMessage, outputMessage);
if (outputValue != null) {
addContentDispositionHeader(inputMessage, outputMessage);
((GenericHttpMessageConverter) messageConverter).write(
outputValue, declaredType, selectedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
"\" using [" + messageConverter + "]");
}
}
return;
}
}
else if (messageConverter.canWrite(valueType, selectedMediaType)) {
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
inputMessage, outputMessage);
if (outputValue != null) {
addContentDispositionHeader(inputMessage, outputMessage);
((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
"\" using [" + messageConverter + "]");
}
}
return;
}
}
}
PathExtensionContentNegotiationStrategy 关键字获取
可以看到这里截取了URI末尾的关键字(json or xml or xhtml ...)
protected String getMediaTypeKey(NativeWebRequest webRequest) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request == null) {
logger.warn("An HttpServletRequest is required to determine the media type key");
return null;
}
String path = this.urlPathHelper.getLookupPathForRequest(request);
String extension = UriUtils.extractFileExtension(path);
return (StringUtils.hasText(extension) ? extension.toLowerCase(Locale.ENGLISH) : null);
}
AbstractMappingContentNegotiationStrategy
public List<MediaType> resolveMediaTypeKey(NativeWebRequest webRequest, String key)
throws HttpMediaTypeNotAcceptableException {
if (StringUtils.hasText(key)) {
//从getDefaultMediaTypes()中寻找匹配
MediaType mediaType = lookupMediaType(key);
if (mediaType != null) {
handleMatch(key, mediaType);
return Collections.singletonList(mediaType);
}
//从servletContext中寻找(我用的是内嵌tomcat,所以用的是MimeMappings)
mediaType = handleNoMatch(webRequest, key);
if (mediaType != null) {
addMapping(key, mediaType);
return Collections.singletonList(mediaType);
}
}
return Collections.emptyList();
}