承接上文调用 HandlerMethod
之后会获取到对应的返回值,对返回值的解析使用的是 HandlerMethodReturnValueHandler
接口,该接口的设计与参数解析器 HandlerMethodArgumentResolver
一模一样,都是组合设计模式,使用树枝节点来组合所有的解析器,下面开始分析.
HandlerMethodReturnValueHandler
中有两个方法,一个是判断是否支持,一个是处理.
public interface HandlerMethodReturnValueHandler { /** * 是否支持当前类型,一般根据类型本身或是注解来判断 */ boolean supportsReturnType(MethodParameter returnType); /** * 处理返回结果,返回结果主要存放到ModelAndViewContainer中 */ void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
HandlerMethodReturnValueHandler
的继承结构大概如下,由于子类众多,所以只列出了几个:
从功能上来分大概会有两类,一种是直接写回数据不需要经过视图解析器 ViewResolver
,其表现是继承了 AbstractMessageConverterMethodProcessor
抽象模板类,一种则是封装到 ModelAndViewContainer
中,转交给视图解析器后再返回,这里的分析重点关注前者.
举个例子开发中常用 @ResponseBody
来返回json信息,其对应的处理器为 RequestResponseBodyMethodProcessor
,该处理器继承了 AbstractMessageConverterMethodProcessor
因此拥有消息转换的能力,该处理不需要经过视图解析器,因此这里是直接利用 MessageConverter
写回数据.
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
进行转换输出.
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默认提供了众多解析器包括如下这些,想要自定义解析器的话可以任意参考其中一个即可.
经过视图解析器的返回处理就很简单了,主要是把视图名还有参数信息设置到 ModelAndViewContainer
容器中,便于视图解析器使用
以 ViewNameMethodReturnValueHandler
为例
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完整的流程结束.