目录
1、handleReturnValue(适配解析结果)
1)、selectHandler(适配返回值解析器-RequestResponseBodyMethodProcessor)
2)、handleReturnValue(解析返回结果)
writeWithMessageConverters(返回值处理)
HttpMessageConverter的write(返回值以输出流输出到客户端或浏览器)
2、getModelAndView
3、上层processDispatchResult(结果处理,回调拦截器的后置方法)
4、总结
在了解完上一篇之后,我们知道整个Spring MVC流程的调用解析过程,里面很多步骤都相当于插件一样,遍历进行适配,最后是调用。而@ResponseBody修饰的Controller中的方法,是比较常用的一种方式,比如Api类型的接口返回的数据,现在基本都是json类型的。其实与ModelAndView本身没有关系了,获取可以理解成,没有视图(View),并且ModelMap也比较特殊,只有一个值。
RequestMappingHandlerAdapter的invokeHandlerMethod方法,在准备好了一切之后,会调用ServletInvocableHandlerMethod的invokeAndHandle方法。在对@ResponseBody方法反射调用后会获取到各种类型的返回值,最后对调用returnValueHandlers的handleReturnValue方法。
在调用之前传入了一个参数(返回值类型),如下:
getReturnValueType(returnValue)
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return new ReturnValueMethodParameter(returnValue);
}
知道new了一个对象,其实可以理解为装饰器模式,将结果进行包装后,拥有了返回值类型等增强功能。
1、handleReturnValue(适配解析结果)
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
handleReturnValue方法解析结果。
1)、selectHandler(适配返回值解析器-RequestResponseBodyMethodProcessor)
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
调用supportsReturnType方法,挨个进行适配。其中还需要判断是否为异常处理类型,当前只分析同步类型。最后适配@ResponseBody类型的为RequestResponseBodyMethodProcessor类型。适配方法如下:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),
ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
}
- Controller类上是否有@ResponseBody注解(则所有方法都有效果)
ReturnValueMethodParameter的getContainingClass方法进行判断,先判断内部属性,当下为null。则判断类上是否有该注解。
public Class<?> getContainingClass() {
Class<?> containingClass = this.containingClass;
return (containingClass != null ? containingClass : getDeclaringClass());
}
- 方法本身(HandlerMethod)上面是否有@ResponseBody注解
如果上面判断类上有则不用判断方法上了,方法HandlerMethod是在调用方法之前在外边就解析好的。直接判断是否有该注解。
@Override
public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) {
return HandlerMethod.this.hasMethodAnnotation(annotationType);
}
2)、handleReturnValue(解析返回结果)
RedirectAttributesMethodArgumentResolver的handleReturnValue方法:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 将处理过的标识设置为true,则后面getModelAndView时会跳过
mavContainer.setRequestHandled(true);
// webRequest包装了request和response, 当前只是对请求和返回再进行包装(装饰器模式)
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 将结果进行处理,其实就是用输出流进行输出
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
1、进入之后,后续会将方法返回的任何类型的结果用OutputStream输入给客户端(浏览器)。所以进来之后直接将处理过的标志设置为true。
ServletServerHttpRequest和ServletServerHttpResponse,装饰器模式肯定是为了新增功能。为后续的将结果使用输出流输出给客户端(浏览器)做准备。
3、将返回值进行处理,其实就是用输出流进行输出
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();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
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);
}
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 (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) {
if (isContentTypePreset) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
1、处理返回值,返回值类型,希望输出的类型(String类型单独进行处理)。
分别是真实的返回值对象,返回值对象的类型,期望返回的类型是@ResponseBody方法上的返回值类型。比如可能期望返回Map,真实返回的是HashMap。
2、如返回值类型是Spring的Resource类型,,,,,则对请求头进行处理和检查(忽略)
3、对Content-Type和MediaType进行处理
1)、如果请求头中有则获取该类型,否则就根据各种情况进行适配
2)、按照适配的类型进行排序
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
3)、获取排序后最适合的类型
4、适配返回类型,进行处理
MediaType相关,比如我们现在基本都是常用application/json。在此前提条件下
StringHttpMessageConverter类型进行处理。
如果为其他Map(HashMap)、或者对象等适配到的是MappingJackson2HttpMessageConverter类型进行处理。
HttpMessageConverter的write(返回值以输出流输出到客户端或浏览器)
上面不论是哪种类型,在处理完header后,最后向上转型为父类HttpMessageConverter调用write方法将返回值用输出流返回给客户端(浏览器)。
@Override
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
final HttpHeaders headers = outputMessage.getHeaders();
addDefaultHeaders(headers, t, contentType);
if (outputMessage instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
@Override
public OutputStream getBody() {
return outputStream;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}));
}
else {
writeInternal(t, outputMessage);
outputMessage.getBody().flush();
}
}
按道理到现在基本完成了@ResponseBody类型的处理,但是框架后续还需要进行一些收尾工作。
2、getModelAndView
考虑各种情况的处理,所以在getModelAndView对视图和数据进行处理,但是允许对处理过的进行跳过,如下:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
// 省略
}
3、上层processDispatchResult(结果处理,回调拦截器的后置方法)
虽然前面将值返回给了客户端,但是后续还会执行processDispatchResult方法处理结果,由于没有返回View(视图),所以打印日志后直接跳过。但还是会对所有的HandlerInteceptor的afterCompletion方法进行回调。
4、总结
handleReturnValue阶段就被RequestResponseBodyMethodProcessor适配处理(适配是先判断类上是否有该注解,否则才会判断方法是否有该注解)。
将处理过的标识设置为true,后续就会跳过处理;并且对Request和Response进行包装;
处理结果时,如果HttpServletRequest的header中有返回值类型则不用在适配了(否则还是遍历适配,排序)。
StringHttpMessageConverter),最后调用write方法将返回值用输出流输出给客户端(或者浏览器)。
不论什么类型,后续在结果处理时,还是会调用HandlerInteceptor的后续处理方法(也就是说该类型是先输出了结果,再处理的拦截器的后置回调方法)。