SpringBoot MVC(6)返回值的处理

826 阅读1分钟

参数的返回

经过参数绑定参数验证之后,调用controller方法,获得返回结果。

InvocableHandlerMethod->invokeForRequest():
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    return doInvoke(args);

返回参数的处理

在参数绑定的准备工作中,ServletInvocableHandlerMethod的resolvers负责请求参数的处理,同样,它的returnValueHandlers负责返回参数的处理。

ServletInvocableHandlerMethod->invokeAndHandle():
    // 这里的returnValue即为返回值,第二个参数是返回值的类型
    this.returnValueHandlers.handleReturnValue(
        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
HandlerMethodReturnValueHandlerComposite->handleReturnValue():
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);

不同的参数+特定的注解会得到不同的解析器,后面绑定参数也不尽相同,下面会对常用的参数类型作举例,至于其他的可以自行研究

RequestResponseBodyMethodProcessor

对应的是方法或类存在@ResponseBody注解的情况,最常用的是@RestController注解,当然也可以像这样写@ResponseBody public String test1(Tree tree))这是SpringBoot中使用最多的一种解析器

RequestResponseBodyMethodProcessor->handleReturnValue():
    // 告诉MVC后面不用再跳转视图了
    mavContainer.setRequestHandled(true);
    ......
    // 调用的是其父类的writeWithMessageConverters()方法
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
AbstractMessageConverterMethodProcessor->writeWithMessageConverters():
    // 获得请求信息头部的accpet信息
    List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
    // 从消息转换器(messageConverters)遍历,如果它支持返回值类型的写入,会将其下的所有媒体类型添加
    List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
    List<MediaType> mediaTypesToUse = new ArrayList<>();
    for (MediaType requestedType : acceptableTypes) {
        for (MediaType producibleType : producibleTypes) {
            if (requestedType.isCompatibleWith(producibleType)) {
                // 筛选出转换器支持与请求符合的媒体类型
                mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
            }
        }
    }
    // 之后mediaTypesToUse排序,得到最符合的媒体类型
    MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
    // 确定返回要使用哪种媒体类型
    // 再次从messageConverters遍历,找到同时支持返回值类型与媒体类型的消息转换器
    ......
    if (body != null) {
        // 以流的形式写入返回数据
        if (genericConverter != null) {
            genericConverter.write(body, targetType, selectedMediaType, outputMessage);
        } else {
            ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
        }
    }
    else {
        return;
    }

不同的参数类型会返回不同的媒体类型,使用的消息转换器也不一样,简单举例(假设没有使用阿里的FastJson,因为一旦使用,消息转换器默认为FastJsonHttpMessageConverter):

参数类型媒体类型消息转换器呈现
基本类型与其包装类application/jsonMappingJackson2HttpMessageConverter基本类型
类类型application/jsonMappingJackson2HttpMessageConverterjson格式
Stringtext/htmlStringHttpMessageConverter字符串
xml(类中存在@XmlRootElement注解)application/xhtml+xmlJaxb2RootElementHttpMessageConverterxml格式

ViewNameMethodReturnValueHandler

对应返回值为String类型,添加返回值为视图名称。如果以redirect:开头,会重定向(redirectModelScenario设为true),其他不做处理

ViewNameMethodReturnValueHandler->handleReturnValue():
    String viewName = returnValue.toString();
    mavContainer.setViewName(viewName);
    if (isRedirectViewName(viewName)) {
        mavContainer.setRedirectModelScenario(true);
    }

ModelAttributeMethodProcessor

对应返回值为类类型,不做处理

ModelAndViewMethodReturnValueHandler

对应返回值为ModelAndView类,与ViewNameMethodReturnValueHandler的处理方法类似