0
点赞
收藏
分享

微信扫一扫

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;

}

我们再来看一下其家族树图示
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_ide
如下所示,有20个类,值得一提的是某些处理器还实现了参数解析器的接口,如ModelMethodProcessor。

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

处理器与返回类型表格

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

处理器

是否解析参数

类型

ModelAndViewMethodReturnValueHandler


ModelAndView

ModelAndViewResolverMethodReturnValueHandler


​直接返回true​

ViewNameMethodReturnValueHandler


Void String CharSequence

ViewMethodReturnValueHandler


View

MapMethodProcessor

​是​

Map

ModelMethodProcessor

​是​

Model

ModelAttributeMethodProcessor

​是​

标注了​​@ModelAttribute​​的方法

RequestResponseBodyMethodProcessor

​是​

标注了​​@ResponseBody​​的方法

HttpEntityMethodProcessor

​是​

HttpEntity ResponseEntity

ResponseBodyEmitterReturnValueHandler


ResponseBodyEmitter

StreamingResponseBodyReturnValueHandler


StreamingResponseBody

​ResponseEntity<StreamingResponseBody>​

HttpHeadersReturnValueHandler


HttpHeaders

CallableMethodReturnValueHandler


Callable

DeferredResultMethodReturnValueHandler


DeferredResult

ListenableFuture

CompletionStage

AsyncTaskMethodReturnValueHandler


WebAsyncTask

HandlerMethodReturnValueHandlerComposite


本身将任务分派给具体的处理器

【2】HandlerMethodReturnValueHandlerComposite

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

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

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

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

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

① supportsReturnType

如下所示,​​HandlerMethodReturnValueHandlerComposite​​​从​​returnValueHandlers​​​中找到一个支持当前返回类型的​​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,那么将会设置​​ModelAndViewContainer​​​ 的​​RequestHandled​​标志位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解​提到过)。
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_重定向_02

② 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​​​,主要是解析​​HttpEntity​​​和​​RequestEntity​​​方法参数并处理​​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类型,实现类如​​ServletRequestAttributes​​​、​​FacesWebRequest​​​等。​​ServletRequestAttributes​​​与​​FacesWebRequest​​​拥有属性​​HttpServletRequest​​​ ​​request​​​、​​HttpServletResponse response​​​与​​HttpSession session​​等。

在其​​createInputMessage​​​方法和​​createOutputMessage​​​中首先获取​​HttpServletRequest​​​ 、​​HttpServletResponse​​​ 然后分别包装成​​ServletServerHttpRequest​​​与​​ServletServerHttpResponse​​返回。所以千万不要别参数名字误导,以为其只是一个单纯的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​​​放入原始​​ServletServerHttpResponse​​​的​​HttpHeaders​​​中。注意,这里对​​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,这里返回结果处理就简单多了。

  • ① 设置请求被处理完毕;
  • ② 获取​​ServletServerHttpRequest​​​ 和​​ServletServerHttpResponse​
  • ③ 进行信息格式转换,然后将转换后的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接口)
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_返回结果处理器_03
我们再顺便看一下这几个接口继承示意与各自提供的方法:

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

SpringMVC常见组件之HandlerMethodReturnValueHandler解析_返回结果处理器_04
我们再顺便看一下这几个接口继承示意与各自提供的方法:

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

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

这里我们先看一下​​writeWithMessageConverters​​​方法执行前各个参数实例。
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_spring mvc_05
我们展开看一下​​​MethodParameter returnType​​​:
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_返回结果处理器_06

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​​​如下所示:
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_返回结果处理器_07
下面这个判断很有意思,如果​​​genericConverter​​​ 不为null,则判断其是否能够进行写操作,否则判断类型转换前的converter是否能够进行写操作。如果都为false,则直接进行下个循环。默认情况下,这里我们会找到​​MappingJackson2HttpMessageConverter​​进行处理。

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

​producibleTypes​​​与排序后的​​mediaTypesToUse​​​:
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_返回结果处理器_08

​GenericHttpMessageConverter​​ 的子类有如下所示:
SpringMVC常见组件之HandlerMethodReturnValueHandler解析_重定向_09

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

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

SpringMVC常见组件之HandlerMethodReturnValueHandler解析_ide_10

【12】ModelAndViewResolverMethodReturnValueHandler

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

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

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

如下所示:其拥有两个属性​​mavResolvers​​和​​modelAttributeProcessor​

@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】的实现。


举报

相关推荐

0 条评论