承接上文调用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);
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 { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
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 { ... 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; } ... } 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默认提供了众多解析器包括如下这些,想要自定义解析器的话可以任意参考其中一个即可.
- MappingJackson2HttpMessageConverter
- GsonHttpMessageConverter
- ByteArrayHttpMessageConverter
- ObjectToStringHttpMessageConverter
- ProtobufHttpMessageConverter
- ResourceHttpMessageConverter
- StringHttpMessageConverter
- 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) { 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完整的流程结束.