Spring MVC--返回值的解析

承接上文调用HandlerMethod之后会获取到对应的返回值,对返回值的解析使用的是HandlerMethodReturnValueHandler接口,该接口的设计与参数解析器HandlerMethodArgumentResolver一模一样,都是组合设计模式,使用树枝节点来组合所有的解析器,下面开始分析.

HandlerMethodReturnValueHandler#

HandlerMethodReturnValueHandler中有两个方法,一个是判断是否支持,一个是处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface HandlerMethodReturnValueHandler {

/**
* 是否支持当前类型,一般根据类型本身或是注解来判断
*/
boolean supportsReturnType(MethodParameter returnType);

/**
* 处理返回结果,返回结果主要存放到ModelAndViewContainer中
*/
void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

HandlerMethodReturnValueHandler的继承结构大概如下,由于子类众多,所以只列出了几个:

从功能上来分大概会有两类,一种是直接写回数据不需要经过视图解析器ViewResolver,其表现是继承了AbstractMessageConverterMethodProcessor抽象模板类,一种则是封装到ModelAndViewContainer中,转交给视图解析器后再返回,这里的分析重点关注前者.

不经过ViewResolver的返回#

举个例子开发中常用@ResponseBody来返回json信息,其对应的处理器为RequestResponseBodyMethodProcessor,该处理器继承了AbstractMessageConverterMethodProcessor因此拥有消息转换的能力,该处理不需要经过视图解析器,因此这里是直接利用MessageConverter写回数据.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
/**
* 根据返回值上的注解决定是否支持解析
*/
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}


@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 直接写回到Response中,因此这里要设置为已处理过,保证总流程中数据解析器不再解析
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

// 使用MessageConverter写回,这个稍后分析
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}

最后解析会调用模板类中的writeWithMessageConverters,该方法主要是选择最适合的转换器,利用HttpMessageConverter进行转换输出.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
// 获取到请求支持的MediaType类型,以及可以产生的MediaType类型
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);

// 选出最合适的类型
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
for (MediaType requestedType : requestedMediaTypes) {
for (MediaType producibleType : producibleMediaTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
...
// 排序然后循环,目的是使用最佳的类型来处理
List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
MediaType.sortBySpecificityAndQuality(mediaTypes);

MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
...
}
// 对HttpMessageConverter做一个循环,直到选出第一个支持的Converter为止
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
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);
}
return;
}
}
...
}
}

if (outputValue != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}

这其中的转换操作使用的是HttpMessageConverter,该类是一个双向的操作,其既可以支持读取,也可以支持写回,因此在Spring MVC中我们可以直接使用@RequestBody接收json字符串格式的Bean,写回主要使用的是其所提供的write()功能.Spring MVC默认提供了众多解析器包括如下这些,想要自定义解析器的话可以任意参考其中一个即可.

  1. MappingJackson2HttpMessageConverter
  2. GsonHttpMessageConverter
  3. ByteArrayHttpMessageConverter
  4. ObjectToStringHttpMessageConverter
  5. ProtobufHttpMessageConverter
  6. ResourceHttpMessageConverter
  7. StringHttpMessageConverter
  8. AllEncompassingFormHttpMessageConverter

经过ViewResolver的返回#

经过视图解析器的返回处理就很简单了,主要是把视图名还有参数信息设置到ModelAndViewContainer容器中,便于视图解析器使用
ViewNameMethodReturnValueHandler为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void handleReturnValue(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());
}
}

然后接着转交给视图解析器ViewResolver解析,主要逻辑会在org.springframework.web.servlet.DispatcherServlet#render中,Spring MVC会从中选择对应的视图解析器,比如thymeleaf就对应ThymeleafViewResolver,jsp则对应JstlViewResolver,具体不在深入.到此算是Spring MVC完整的流程结束.

设计模式--观察者模式的思考
Spring MVC--异常处理