在HttpMessageConverter的注册咱们知道了: DispatcherServlet#service-->DispatcherServlet#doService-->DispatcherServlet#doDispatch-->HandlerAdapter#handle-->AbstractHandlerMethodAdapter#handleInternalhtml
这里咱们看一个更加具体的调用流程图:java
RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter,因此当使用RequestMappingHandlerAdapter的时候,最终调用的就是RequestMappingHandlerAdapter的handleInternal函数。web
RequestMappingHandlerAdapter的handleInternal函数调用了,RequestMappingHandlerAdapter的invokeHandlerMethod。服务器
incokeHandlerMethod函数中建立了一个ServletInvocableHandlerMethodapp
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); }
如上所示是RequestMappingHandlerAdapter的incokeHandlerMethod函数的部分代码,咱们能够看到建立ServletInvocableHandlerMethod的时候传入的参数类型是: HandlerMethodArgumentResolverComposite HandlerMethodReturnValueHandlerCompositeide
HandlerMethodArgumentResolverComposite、HandlerMethodReturnValueHandlerComposite是在RequestMappingHandlerAdapter初始化的时候建立的是默认值。能够看RequestMappingHandlerAdapter的afterPropertiesSet函数。函数
HandlerMethodArgumentResolverComposite和HandlerMethodReturnValueHandlerComposite是能够从新设置的,可是必须是使用List<HandlerMethodArgumentResolver>和List<HandlerMethodReturnValueHandler>这样列表的形式,不支持单个添加。this
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
如上是ServletInvocableHandlerMethod的invokeAndHandle函数的部分代码,invokeForRequest方法是执行的真正的调用逻辑部分,通常也就是Controller中的方法封装为InvocableHandlerMethod,InvocableHandlerMethod的doInvoke执行调用,若是有桥接方法,就调用的桥接方法。spa
另外,咱们能够看到调用的是HandlerMethodReturnValueHandlerComposite的handleReturnValue,这算是比较接近抽象层了。.net
HandlerMethodReturnValueHandlerComposite的逻辑就很是清晰,handleReturnValue函数就是检查List<HandlerMethodReturnValueHandler>列表中哪个HandlerMethodReturnValueHandler支持处理返回值(经过调用HandlerMethodReturnValueHandler接口的supportsReturnType函数)。
最多见的咱们使用ResponseBody注解的时候就会使用到的HandlerMethodReturnValueHandler实现类RequestResponseBodyMethodProcessor。
这里咱们就看使用最多的RequestResponseBodyMethodProcessor的handleReturnValue函数。
它调用了AbstractMessageConverterMethodProcessor的writeWithMessageConverters函数,如今咱们重点的来看一下AbstractMessageConverterMethodProcessor的writeWithMessageConverters函数。
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; Class<?> valueType; Type targetType; if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } if (isResourceType(value, returnType)) { outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) { Resource resource = (Resource) value; try { List<HttpRange> httpRanges = inputMessage.getHeaders().getRange(); outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value()); body = HttpRange.toResourceRegions(httpRanges, resource); valueType = body.getClass(); targetType = RESOURCE_REGION_LIST_TYPE; } catch (IllegalArgumentException ex) { outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength()); outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); } } } MediaType selectedMediaType = null; MediaType contentType = outputMessage.getHeaders().getContentType(); if (contentType != null && contentType.isConcrete()) { if (logger.isDebugEnabled()) { logger.debug("Found 'Content-Type:" + contentType + "' in response"); } selectedMediaType = contentType; } else { HttpServletRequest request = inputMessage.getServletRequest(); List<MediaType> acceptableTypes = getAcceptableMediaTypes(request); List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType); if (body != null && producibleTypes.isEmpty()) { throw new HttpMessageNotWritableException( "No converter found for return value of type: " + valueType); } List<MediaType> mediaTypesToUse = new ArrayList<>(); for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (mediaTypesToUse.isEmpty()) { if (body != null) { throw new HttpMediaTypeNotAcceptableException(producibleTypes); } if (logger.isDebugEnabled()) { logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes); } return; } MediaType.sortBySpecificityAndQuality(mediaTypesToUse); for (MediaType mediaType : mediaTypesToUse) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (logger.isDebugEnabled()) { logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes); } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> converter : this.messageConverters) { GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]"); addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } if (body != null) { throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } }
如上所示是AbstractMessageConverterMethodProcessor的writeWithMessageConverters函数,比较长,可见逻辑是比较复杂的。
虽然有点复杂,可是流程仍是很是清晰的,开始先检查和获取返回值的类型和返回值目标类型。
接下来是肯定肯定使用MediaType,首先:
MediaType contentType = outputMessage.getHeaders().getContentType();
从Response的Header中找有没有设置Content-Type,若是有就直接使用该Content-Type对应的MediaType。
若是没有就先获取请求接受的MediaType,就是从请求头中的Accept中解析MediaType:
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
再找服务器端支持的MediaType:
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
查找的方式就是先从Request找看有没有设置MediaType,若是没有设置就找HttpMessageConverter列表中支持的全部MediaType。
而后就一次匹配,就是找到全部客户端可以Accept的MediaType,而且服务端也支持的:
for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } }
若是一个都没有找到,而且返回值不为null,那么就直接抛出异常。就是比较常见的HttpMediaTypeNotAcceptableException异常。
上面一波操做可能找到多个MediaType,咋整呢?排个序。
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
咱们常常在Accept中看到下面的内容:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
以前一直不知道q=0.8这类的东西是干什么用的,如今看源码就知道了,排序用的,就是设置优先级,q就是quality的简写。
找到一个MediaType就又能够开始愉快的玩耍了,就是遍历最开始咱们设置的HttpMessageConverter列表。找到一个支持MediaType的Converter。
咋找呢HttpMessageConverter的canWrite接口就是用来干这事情的。
找到了HttpMessageConverter接口的write就有用武之地了。固然得先看一下有没有RequestResponseBodyAdvice这种东西,有的话就先执行一下RequestResponseBodyAdvice的beforeBodyWrite。