SpringMVC常见组件之HandlerMethodReturnValueHandler解析

在前面我们分析SpringMVC常见组件之HandlerAdapter分析中提到过如下过程:

RequestMappingHandlerAdapter.invokeAndHandle(webRequest, mavContainer);
--ServletInvocableHandlerMethod.invokeAndHandle(webRequest, mavContainer);
---`Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);`
---this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

其中很重要的一步就是在HandlerMethodReturnValueHandlerComposite中解析方法返回结果,方法源码如下所示:

@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);
}

看到这里是不是就联想到了SpringMVC常见组件之HandlerMethodArgumentResolver解析中的HandlerMethodArgumentResolverComposite?没错,都是策略接口,应用了组合模式和中介者模式,将动作委派给具体的handler处理。

【1】HandlerMethodReturnValueHandler

方法返回结果处理器,其是一个策略接口,提供了两个方法让子类实现:supportsReturnType用来判断当前返回结果处理器是否能够处理返回结果,handleReturnValue方法用来处理返回结果。

public interface HandlerMethodReturnValueHandler {

	 // 当前handler是否能够处理 MethodParameter returnType
	boolean supportsReturnType(MethodParameter returnType);

	 //向model添加数据并设置view或者设置响应已经处理
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

我们再来看一下其家族树图示
在这里插入图片描述
如下所示,有20个类,值得一提的是某些处理器还实现了参数解析器的接口,如ModelMethodProcessor。

  • ViewNameMethodReturnValueHandler
  • MapMethodProcessor
  • ViewMethodReturnValueHandler
  • StreamingResponseBodyReturnValueHandler
  • DeferredResultMethodReturnValueHandler
  • HandlerMethodReturnValueHandlerComposite
  • HttpHeadersReturnValueHandler
  • CallableMethodReturnValueHandler
  • ModelMethodProcessor
  • ModelAttributeMethodProcessor
    • ServletModelAttributeMethodProcessor
  • ResponseBodyEmitterReturnValueHandler
  • ModelAndViewMethodReturnValueHandler
  • ModelAndViewResolverMethodReturnValueHandler
  • AbstractMessageConverterMethodProcessor
    • RequestResponseBodyMethodProcessor
    • HttpEntityMethodProcessor
  • AsyncHandlerMethodReturnValueHandler
  • AsyncTaskMethodReturnValueHandler

处理器与返回类型表格

如下表格中“是否解析参数”,也就是说其同时实现了HandlerMethodArgumentResolver接口,可以解析参数。

处理器是否解析参数类型
ModelAndViewMethodReturnValueHandlerModelAndView
ModelAndViewResolverMethodReturnValueHandler直接返回true
ViewNameMethodReturnValueHandlerVoid String CharSequence
ViewMethodReturnValueHandlerView
MapMethodProcessorMap
ModelMethodProcessorModel
ModelAttributeMethodProcessor标注了@ModelAttribute的方法
RequestResponseBodyMethodProcessor标注了@ResponseBody的方法
HttpEntityMethodProcessorHttpEntity ResponseEntity
ResponseBodyEmitterReturnValueHandlerResponseBodyEmitter
StreamingResponseBodyReturnValueHandlerStreamingResponseBody
ResponseEntity<StreamingResponseBody>
HttpHeadersReturnValueHandlerHttpHeaders
CallableMethodReturnValueHandlerCallable
DeferredResultMethodReturnValueHandlerDeferredResult
ListenableFuture
CompletionStage
AsyncTaskMethodReturnValueHandlerWebAsyncTask
HandlerMethodReturnValueHandlerComposite本身将任务分派给具体的处理器

【2】HandlerMethodReturnValueHandlerComposite

通过将处理动作委派给内部注册的一系列HandlerMethodReturnValueHandler来实现功能。其内部有个常量集合如下:

private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();

其首先应用了组合模式,无论ServletInvocableHandlerMethod调用HandlerMethodReturnValueHandlerComposite还是单个具体的HandlerMethodReturnValueHandler,其行为都是一致的。

什么行为?第一是判断是否支持当前返回类型也就是supportsReturnType方法;第二就是处理返回结果的方法handleReturnValue

其次应用了委派/策略模式,InvocableHandlerMethod在处理返回结果的时候根本不知道也不关心具体的HandlerMethodReturnValueHandler是谁,其根据supportsReturnType方法从returnValueHandlers中筛选一个合适的处理器进行结果处理。

① supportsReturnType

如下所示,HandlerMethodReturnValueHandlerCompositereturnValueHandlers中找到一个支持当前返回类型的handler,然后返回该HandlerMethodReturnValueHandler 。也就是将supportsReturnType的动作委派给了returnValueHandlers中的一个个具体的处理器。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return getReturnValueHandler(returnType) != null;
}
@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
	for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
		if (handler.supportsReturnType(returnType)) {
			return handler;
		}
	}
	return null;
}

② handleReturnValue

如下所示,HandlerMethodReturnValueHandlerComposite首先从returnValueHandlers拿到一个合适的HandlerMethodReturnValueHandler 然后使用该处理器进行返回结果处理。如果没有找到合适的HandlerMethodReturnValueHandler ,将会抛出异常。

@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
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

// 找到一个合适的HandlerMethodReturnValueHandler 
@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;
}

【3】ModelAndViewMethodReturnValueHandler

经典的视图处理器,处理返回结果类型为ModelAndView的值。该处理器将会把ModelAndView中的View信息和Model信息拷贝到ModelAndViewContainer中。如果返回结果为null,那么将会设置ModelAndViewContainerRequestHandled标志位true表名请求已经被直接处理完毕。

返回结果是ModelAndView类型的是有一种固定的用途,因此ModelAndViewMethodReturnValueHandler应该被配置在某些处理器前面(支持标注了@ModelAttribute@ResponseBody注解的方法的返回结果类型的),以免被覆盖。

① supportsReturnType

异常简单,如下所示判断返回结果类型是否为ModelAndView。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

代码如下所示,首先判断返回值是否为空,如果为空则设置请求处理标志位为true表示当前请求已经被处理,返回返回。

接下来根据mav中的view是否是String进行不同的处理。这里需要注意的是进行了是否为重定向判断,如果是重定向如redirect:/user/list,那么将会设置RedirectModelScenario为true。

最后设置status并将mav中的model属性都放到了mavContainer中。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 判断是否为空
	if (returnValue == null) {
		mavContainer.setRequestHandled(true);
		return;
	}
	
	ModelAndView mav = (ModelAndView) returnValue;
	// 判断当前mav中的view是否为String
	if (mav.isReference()) {
		String viewName = mav.getViewName();
		mavContainer.setViewName(viewName);
		if (viewName != null && isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else {
		View view = mav.getView();
		mavContainer.setView(view);
		if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	//设置status
	mavContainer.setStatus(mav.getStatus());
	// 放入model数据
	mavContainer.addAllAttributes(mav.getModel());
}

【4】ModelMethodProcessor

其是一个参数解析器&&返回结果处理器,解析Model类型的参数并处理Model类型的返回结果。

ModelAndViewMethodReturnValueHandler一样,ModelMethodProcessor应该被配置在某些处理器前面(支持标注了@ModelAttribute@ResponseBody注解的方法的返回结果类型的),以免被覆盖。

① supportsReturnType

判断方法如下所示,返回当前类型是否是Model。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return Model.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue == null) {
		return;
	}
	else if (returnValue instanceof Model) {
		mavContainer.addAllAttributes(((Model) returnValue).asMap());
	}
	else {
		// should not happen
		throw new UnsupportedOperationException("Unexpected return type [" +
				returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
	}
}

代码解释如下:

  • ① 如果returnValue是null,则直接返回;
  • ② 如果返回值是Model类型,则将model里面数据放入mavContainer;
  • ③ 否则,抛出UnsupportedOperationException异常

【5】MapMethodProcessor

解析Map类型参数并能够处理Map类型的返回结果。

由于加了注解@ModelAttribute或者@ResponseBody方法也可能返回Map类型,因为在5.2版本后,这个解析器不处理标注了注解的方法参数。

① supportsReturnType

如下所示,判断返回类型是否为Map。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return Map.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

方法如下所示,如果返回类型是map,则将map放入mavContainer中。否则如果返回结果不为null,则抛出UnsupportedOperationException异常。

@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue instanceof Map){
		mavContainer.addAllAttributes((Map) returnValue);
	}
	else if (returnValue != null) {
		// should not happen
		throw new UnsupportedOperationException("Unexpected return type [" +
				returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
	}
}

【6】ViewMethodReturnValueHandler

与视图相关,处理返回结果类型为View的。如果返回值为null,则由配置的RequestToViewNameTranslator按照约定选择视图名称。

ModelAndViewMethodReturnValueHandler一样,ViewMethodReturnValueHandler应该被配置在某些处理器**前面(**支持标注了@ModelAttribute@ResponseBody注解的方法的返回结果类型的),以免被覆盖。

① supportsReturnType

如下所示,ViewMethodReturnValueHandler只处理返回结果类型为View的。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return View.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

在处理返回结果的时候,会判断返回结果是否为View类型,然后设置为mavContainer中的View引用。如果其是RedirectView则设置重定向模型场景标志为true,表示当前时重定向请求。如果其不是View类型并且结果不为null,则抛出UnsupportedOperationException异常。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue instanceof View) {
		View view = (View) returnValue;
		mavContainer.setView(view);
		if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else if (returnValue != null) {
		// should not happen
		throw new UnsupportedOperationException("Unexpected return type: " +
				returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
	}
}

【7】ViewNameMethodReturnValueHandler

与视图相关,处理返回结果为void、String以及基本的CharSequence。如果返回值为null,则由配置的RequestToViewNameTranslator按照约定选择视图名称。需要注意的是,ViewNameMethodReturnValueHandler应该被配置在某些处理器**后面(**支持标注了@ModelAttribute@ResponseBody`注解的方法的返回结果类型的),以免覆盖。

① supportsReturnType

支持void类型或者CharSequence类型如String、StringBuilder。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	Class<?> paramType = returnType.getParameterType();
	return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}

② handleReturnValue

如果返回结果不是CharSequence类型并且不为null,则直接抛出异常。如果返回结果是CharSequence类型,则设置mavContainer的viewName并尝试判断是否为重定向view而设置RedirectModelScenario为true。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue instanceof CharSequence) {
		String viewName = returnValue.toString();
		mavContainer.setViewName(viewName);
		if (isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else if (returnValue != null) {
		// should not happen
		throw new UnsupportedOperationException("Unexpected return type: " +
				returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
	}
}

【8】ModelAttributeMethodProcessor

ModelAttributeMethodProcessor与子类ServletModelAttributeMethodProcessor主要用来处理标注了@ModelAttribute注解的方法,解析方法的参数并处理方法的返回结果。如果属性annotationNotRequired被设置为true,那么非简单类型的参数或返回结果将会被该处理器默认处理。

① supportsReturnType

处理标注了@ModelAttribute注解的返回结果或者属性annotationNotRequired 为true并且返回结果类型为非简单类型。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return (returnType.hasMethodAnnotation(ModelAttribute.class) ||
			(this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType())));
}

什么是简单类型?如下图所示(前面我们在SpringMVC常见组件之HandlerMethodArgumentResolver解提到过)。
在这里插入图片描述

② handleReturnValue

如下所示如果返回结果不为null,则根据返回结果值与类型获取name,然后放到mavContainer的model中。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue != nul	l) {
		String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
		mavContainer.addAttribute(name, returnValue);
	}
}

获取name的方法如下所示,如果有注解ModelAttribute并且注解value属性不为null,则使用注解的value属性。否则的话根据方法与所属类获取返回结果类型Class,进而生成一个name。

public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
	ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
	if (ann != null && StringUtils.hasText(ann.value())) {
		return ann.value();
	}
	else {
		Method method = returnType.getMethod();
		Assert.state(method != null, "No handler method");
		Class<?> containingClass = returnType.getContainingClass();
		Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
		return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
	}
}

【9】HttpHeadersReturnValueHandler

① supportsReturnType

处理返回结果类型是HttpHeaders的。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return HttpHeaders.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

@Override
@SuppressWarnings("resource")
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	mavContainer.setRequestHandled(true);

	Assert.state(returnValue instanceof HttpHeaders, "HttpHeaders expected");
	HttpHeaders headers = (HttpHeaders) returnValue;

	if (!headers.isEmpty()) {
		HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
		Assert.state(servletResponse != null, "No HttpServletResponse");
		ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(servletResponse);
		outputMessage.getHeaders().putAll(headers);
		outputMessage.getBody();  // flush headers
	}
}

方法解释如下:

  • ① 设置当前请求已经被处理mavContainer.setRequestHandled(true);
  • ② 判断返回结果必须为HttpHeaders类型,否则抛出IllegalStateException异常;
  • ③ 如果headers不为空,则获取ServletServerHttpResponse 然后将headers添加到响应头中;
  • ④ 调用outputMessage.getBody();这里会进一步调用writeHeaders();,也就是所谓的flush headers。

【10】HttpEntityMethodProcessor

HttpEntityMethodProcessor继承自AbstractMessageConverterMethodProcessor,主要是解析HttpEntityRequestEntity方法参数并处理HttpEntity类型和ResponseEntity类型的返回结果。

ModelAndViewMethodReturnValueHandler一样,HttpEntityMethodProcessor应该被配置在某些处理器**前面(**支持标注了@ModelAttribute@ResponseBody注解的方法的返回结果类型的),以免被覆盖。

① supportsReturnType

返回结果是HttpEntity类型并且不是RequestEntity子类型(RequestEntity是HttpEntity的子类)。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
			!RequestEntity.class.isAssignableFrom(returnType.getParameterType()));
}

② handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	// 设置请求已经被处理
	mavContainer.setRequestHandled(true);
	// 如果返回结果为null,直接返回
	if (returnValue == null) {
		return;
	}
	// 根据NativeWebRequest获取ServletServerHttpRequest
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	//根据NativeWebRequest获取ServletServerHttpResponse 
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
	
	//如果不是HttpEntity类型则抛出异常
	Assert.isInstanceOf(HttpEntity.class, returnValue);	
	HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;

	// 获取原始响应头
	HttpHeaders outputHeaders = outputMessage.getHeaders();
	// 获取返回结果的头信息
	HttpHeaders entityHeaders = responseEntity.getHeaders();
	// 如果返回结果头信息不为空,则将其放入原始响应头中。注意这里对VARY进行了特殊处理
	if (!entityHeaders.isEmpty()) {
		entityHeaders.forEach((key, value) -> {
			if (HttpHeaders.VARY.equals(key) && outputHeaders.containsKey(HttpHeaders.VARY)) {
				List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders);
				if (!values.isEmpty()) {
					outputHeaders.setVary(values);
				}
			}
			else {
				outputHeaders.put(key, value);
			}
		});
	}
	// 如果返回结果是ResponseEntity类型,根据200或者3XX进行不同处理
	if (responseEntity instanceof ResponseEntity) {
		int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
		outputMessage.getServletResponse().setStatus(returnStatus);
		if (returnStatus == 200) {
			HttpMethod method = inputMessage.getMethod();
			if ((HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method))
					&& isResourceNotModified(inputMessage, outputMessage)) {
				outputMessage.flush();
				return;
			}
		}
		else if (returnStatus / 100 == 3) {
			String location = outputHeaders.getFirst("location");
			if (location != null) {
				saveFlashAttributes(mavContainer, webRequest, location);
			}
		}
	}

	// Try even with null body. ResponseBodyAdvice could get involved.
	writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage);

	// Ensure headers are flushed even if no body was written.
	outputMessage.flush();
}

方法首先进行了前置处理,设置RequestHandled为true标明请求已经处理完毕。如果判断返回结果为null则直接返回。

下面两行代码很有意思,这里webRequest是NativeWebRequest类型,实现类如ServletRequestAttributesFacesWebRequest等。ServletRequestAttributesFacesWebRequest拥有属性HttpServletRequest requestHttpServletResponse responseHttpSession session等。

在其createInputMessage方法和createOutputMessage中首先获取HttpServletRequestHttpServletResponse 然后分别包装成ServletServerHttpRequestServletServerHttpResponse返回。所以千万不要别参数名字误导,以为其只是一个单纯的request。

ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
	HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
	Assert.state(servletRequest != null, "No HttpServletRequest");
	return new ServletServerHttpRequest(servletRequest);
}

protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) {
	HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
	Assert.state(response != null, "No HttpServletResponse");
	return new ServletServerHttpResponse(response);
}

然后就是对entityHeaders进行了处理,基本就是把entityHeaders放入原始ServletServerHttpResponseHttpHeaders中。注意,这里对HttpHeaders.VARY做了特殊处理。

如果返回结果是ResponseEntity类型,那么根据状态码是200或者3XX系列(重定向)进行不同处理。

  • 如果status是200,且方法是(GET方法或HEAD方法)&&ResourceNotModified,那么会调用响应的flush方法(写入响应头、刷空缓冲区);
  • 如果状态码为3XX,获取响应头中的location。如果location不为空,则进行“闪存属性”处理。简单来说就是重定向属性的更新。

writeWithMessageConverters这一步很关键,会进行message的转换,也就是所谓的格式化。将响应内容转换为需要的格式。

outputMessage.flush();会确保headers被flushed即时响应体为空。这里我们可以看下flush方法,如下所示其首先会处理header。然后判断当前body是否被使用,如果被使用则调用flushBuffer方法将缓冲区的内容刷到客户端。

@Override
public void flush() throws IOException {
	writeHeaders();
	if (this.bodyUsed) {
		this.servletResponse.flushBuffer();
	}
}

【11】RequestResponseBodyMethodProcessor

你所使用的@RequestBody注解或者@ResponseBody注解起作用就是该Processor在工作。该Processor会处理标注了@RequestBody的方法参数和标注了@ResponseBody注解的方法返回结果。其将会从请求中读入信息并将返回结果写入到响应体中,在这其中会使用HttpMessageConverter进行必要的格式转换。

如果使用@Valid注解,那么@RequestBody的方法参数同样被校验。如果配置了DefaultHandlerExceptionResolver,那么校验失败时会抛出状态码为400的异常。

① supportsReturnType

使用了@ResponseBody注解或者方法标注了@ResponseBody注解。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
			returnType.hasMethodAnnotation(ResponseBody.class));
}

② handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	mavContainer.setRequestHandled(true);
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

	// Try even with null return value. ResponseBodyAdvice could get involved.
	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

相比HttpEntityMethodProcessor,这里返回结果处理就简单多了。

  • ① 设置请求被处理完毕;
  • ② 获取ServletServerHttpRequestServletServerHttpResponse
  • ③ 进行信息格式转换,然后将转换后的message写入到响应体中,最后会调用outputMessage.getBody().flush();方法,将缓冲区的内容刷冲到客户端。

③ createInputMessage

我们看一下其ServletServerHttpRequest inputMessage = createInputMessage(webRequest);。如下所示其根据NativeWebRequest 获取到HttpServletRequest 然后包装为ServletServerHttpRequest实例对象返回。

protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
	HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
	Assert.state(servletRequest != null, "No HttpServletRequest");
	return new ServletServerHttpRequest(servletRequest);
}

如下图所示,ServletServerHttpRequest 实现了ServerHttpRequest接口(后者继承了HttpRequest, HttpInputMessage接口)
在这里插入图片描述
我们再顺便看一下这几个接口继承示意与各自提供的方法:

public interface HttpMessage {
	HttpHeaders getHeaders();
}
public interface HttpInputMessage extends HttpMessage {
	InputStream getBody() throws IOException;
}
public interface HttpRequest extends HttpMessage {
	@Nullable
	default HttpMethod getMethod() {
		return HttpMethod.resolve(getMethodValue());
	}
	String getMethodValue();
	URI getURI();
}
public interface ServerHttpRequest extends HttpRequest, HttpInputMessage {
	@Nullable
	Principal getPrincipal();
	InetSocketAddress getLocalAddress();
	InetSocketAddress getRemoteAddress();
	ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response);
}

④ createOutputMessage(webRequest)

我们继续看一下ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);,放入如下所示根据NativeWebRequest 拿到HttpServletResponse (我们经常使用的),然后作为构造函数入参实例化一个ServletServerHttpResponse对象。

protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) {
	HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
	Assert.state(response != null, "No HttpServletResponse");
	return new ServletServerHttpResponse(response);
}

在这里插入图片描述
我们再顺便看一下这几个接口继承示意与各自提供的方法:

public interface HttpMessage {
	HttpHeaders getHeaders();
}
public interface HttpOutputMessage extends HttpMessage {
	OutputStream getBody() throws IOException;

}
public interface ServerHttpResponse extends HttpOutputMessage, Flushable, Closeable {
	void setStatusCode(HttpStatus status);
	@Override
	void flush() throws IOException;
	@Override
	void close();
}

⑤ writeWithMessageConverters

HttpEntityMethodProcessorRequestResponseBodyMethodProcessor都是AbstractMessageConverterMethodProcessor的子类,其继承自AbstractMessageConverterMethodArgumentResolver,提供了能力-使用HttpMessageConverter处理方法返回结果,将转换后的结果写入到response。

这里我们先看一下writeWithMessageConverters方法执行前各个参数实例。
在这里插入图片描述
我们展开看一下MethodParameter returnType
在这里插入图片描述

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
		ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	Object body;
	Class<?> valueType;
	Type targetType;
// 如果返回结果是String
	if (value instanceof CharSequence) {
		body = value.toString();
		valueType = String.class;
		targetType = String.class;
	}
	else {// 返回结果不是string
		body = value;
		// 获取值类型
		valueType = getReturnValueType(body, returnType);
		// 获取返回结果目标类型
		targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
	}
// 如果返回结果类型是Resource,但不是InputStreamResource
	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;
	// 获取响应的contentType 
	MediaType contentType = outputMessage.getHeaders().getContentType();
	// 判断contentType是不是具体的--即不带通配符*
	boolean isContentTypePreset = contentType != null && contentType.isConcrete();
	if (isContentTypePreset) {
		if (logger.isDebugEnabled()) {
			logger.debug("Found 'Content-Type:" + contentType + "' in response");
		}
		// 如果返回结果是具体的,则直接赋予selectedMediaType 
		selectedMediaType = contentType;
	}
	else {
	// 否则就要根据acceptableTypes 与producibleTypes 选择一个合适的selectedMediaType 
		HttpServletRequest request = inputMessage.getServletRequest();
		// 通过contentNegotiationManager解析request获取mediaType,其实就是根据HttpHeaders.ACCEPT
		List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
		// 1.从request中获取HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE属性对应的值;
		// 2.获取能够对当前返回结果进行写操作的converter支持的mediaType
		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));
				}
			}
		}
		// 没有找到能够使用的mediaTypes,就抛出异常HttpMediaTypeNotAcceptableException
		if (mediaTypesToUse.isEmpty()) {
			if (body != null) {
				throw new HttpMediaTypeNotAcceptableException(producibleTypes);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
			}
			return;
		}
		//对mediaTypesToUse进行排序 具体的在前
		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);
		}
	}
// 如果selectedMediaTypebuw
	if (selectedMediaType != null) {
		// 移除掉mediaType中的q
		selectedMediaType = selectedMediaType.removeQualityValue();
		// 遍历messageConverters 找到一个合适的HttpMessageConverter
		for (HttpMessageConverter<?> converter : this.messageConverters) {
		 // 判断当前converter是不是GenericHttpMessageConverter类型
			GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
					(GenericHttpMessageConverter<?>) converter : null);
			if (genericConverter != null ?
					((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
					converter.canWrite(valueType, selectedMediaType)) {
					// 在向outputmessage写入body前,获取adviceBean,对响应结果进行处理
				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) + "]");
							// 尝试添加HttpHeaders.CONTENT_DISPOSITION
					addContentDispositionHeader(inputMessage, outputMessage);
					if (genericConverter != null) {
					// 使用genericConverter 处理body然后写入到outputmessage
						genericConverter.write(body, targetType, selectedMediaType, outputMessage);
					}
					else {
					// 使用HttpMessageConverter处理body然后写入到outputmessage
						((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
					}
				}
				else {
					if (logger.isDebugEnabled()) {
						logger.debug("Nothing to write: null body");
					}
				}
				return;
			}
		}
	}
// 这里只会抛出异常
	if (body != null) {
		Set<MediaType> producibleMediaTypes =
				(Set<MediaType>) inputMessage.getServletRequest()
						.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

		if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
			throw new HttpMessageNotWritableException(
					"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
		}
		throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
	}
}

this.messageConverters如下所示:
在这里插入图片描述
下面这个判断很有意思,如果genericConverter 不为null,则判断其是否能够进行写操作,否则判断类型转换前的converter是否能够进行写操作。如果都为false,则直接进行下个循环。默认情况下,这里我们会找到MappingJackson2HttpMessageConverter进行处理。

if (genericConverter != null ?
					((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
					converter.canWrite(valueType, selectedMediaType))

producibleTypes与排序后的mediaTypesToUse:
在这里插入图片描述

GenericHttpMessageConverter 的子类有如下所示:
在这里插入图片描述

如下图所示,当writeWithMessageConverters方法中下面代码执行完,客户端就会拿到响应结果。此时整个请求流程对springmvc来说还未结束!!!

if (genericConverter != null) {
	genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
	((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}

在这里插入图片描述

【12】ModelAndViewResolverMethodReturnValueHandler

这个处理器比较有意思,其supportsReturnType仅仅只是返回true。也就意味着,这是最后的保留手段。所以该处理器是配置在所有处理器的后面。

返回值可以使用ModelAndViewResolver处理。如果是非简单类型,则可以将其视为模型属性(model attribute使用modelAttributeProcessor处理)。如果这两种方法都不成功(本质上是简单类型,而不是字符串),则会引发UnsupportdOperationException。

需要注意的是,ModelAndViewResolver只是一个接口,目前并没有实现类。所以你可以实现ModelAndViewResolver将其作为一个HandlerMethodReturnValueHandler。

如下所示:其拥有两个属性mavResolversmodelAttributeProcessor

@Nullable
private final List<ModelAndViewResolver> mavResolvers;

private final ModelAttributeMethodProcessor modelAttributeProcessor =
		new ServletModelAttributeMethodProcessor(true);
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (this.mavResolvers != null) {
		for (ModelAndViewResolver mavResolver : this.mavResolvers) {
			Class<?> handlerType = returnType.getContainingClass();
			Method method = returnType.getMethod();
			Assert.state(method != null, "No handler method");
			ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
			// 解析得到MAV
			ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, webRequest);
			if (mav != ModelAndViewResolver.UNRESOLVED) {
			 //将属性放入mavContainer
				mavContainer.addAllAttributes(mav.getModel());
				//为mavContainer设置viewName
				mavContainer.setViewName(mav.getViewName());
				if (!mav.isReference()) {
					//设置view
					mavContainer.setView(mav.getView());
				}
				return;
			}
		}
	}

	// No suitable ModelAndViewResolver...
	if (this.modelAttributeProcessor.supportsReturnType(returnType)) {
		this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
	else {
		throw new UnsupportedOperationException("Unexpected return type: " +
				returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
	}
}

方法如上所示,先尝试使用ModelAndViewResolver 进行处理,然后使用modelAttributeProcessor进行处理。默认ModelAndViewResolver没有实现类,所以通常这里是将动作委派给具体的modelAttributeProcessor进行处理。

如果你想实现一个ModelAndViewResolver,那么考虑本文中【3】、【4】、【5】、【6】、【7】、【8】的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值