【Spring MVC】MVC请求的处理过程一览-HandlerMethodReturnValueHandler结果处理器

1  前言

上节【Spring MVC】MVC请求的处理过程一览-HandlerMethodArgumentResolver解析参数过程【二】上两节我们看了总共8种常用参数的解析器,那我们本节继续看看结果的处理器。

2  HandlerMethodReturnValueHandler

2.1  概述

HandlerMethodReturValueHandler 用在 ServletinvocableHandlerMethod 中,作用是处理处理器执行后的返回值,主要有三个功能:①将相应参数添加到 Model;②设置 View;③如果请求已经处理完则设置 ModelAndViewContainer 的 requestHandled true。

HandlerMethodReturnValueHandler 的使用方式与 HandlerMethodArgumentResolver 非常相似,接口里也是两个方法,一个用于判断是否支持,一个用于具体处理返回值,接口定义如下:

public interface HandlerMethodReturnValueHandler {

    // 是否支持
    boolean supportsReturnType(MethodParameter returnType);

    /**
     * 处理结果
     * @param returnValue 返回结果
     * @param returnType 结果类型
     * @param mavContainer 上下文传递对象
     * @param webRequest 请求对象
     * @throws Exception 异常
     */
    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

我们看下他的实现类:

还是比较多的,接下来我们简单介绍下这些处理器,然后看几个我们平时常用的结果处理器。

2.2  处理器分类

实现类中有一个特殊的类,那就是 HandlerMethodReturnValueHandlerComposite,它和前面说的 HandlerMethodArgumentResolverComposite 功能相同,自己不具体处理返回值,而是使用内部封装的组件进行处理。其他实现类都和具体返回值类型相对应。

下面依次介绍这些处理器:

AbstractMessageConverterMethodProcessor:处理返回值需要使用 HttpMessageConverter写入 response 的基类,自己并未具体做处理,而是定义了相关工具。

HttpEntityMethodProcessor:处理HttpEntity 类型,并且不是 RequestEntity 类型的返回值。

RequestResponseBodyMethodProcessor :处理当返回值或者处理请求的Handler 类注释了 @ResponseBody 情况下的返回值,这个是我们自从前后端分离后,基本最常用的处理器了。我们的 Controller 类基本都会 @RestController 而他上边就有 @ResponseBody 注解。

AsyncTaskMethodReturnValueHandler:处理WebAsyncTask 类型的返回值,用于异步请求,使用 WebAsyncManager 完成。

CallableMethodReturnValueHandler:处理 Callable 类型的返回值,用于异步请求,使用 WebAsyncManager 完成。

DeferredResultMethodReturnValueHandler:处理 DeferredResult类型的返回值,用于异步请求,使用 WebAsyncManager 完成。

HandlerMethodReturnValueHandlerComposite: 用于封装其他处理器,方便调用。

HttpHeadersReturnValueHandler :处理 HttpHeaders 类型的返回值,将 HttpHeaders 的返回值添加到 response 的Headers并设置 mavContainer 的 requestHandled 为 true。

ListenableFutureReturValueHlandler :处理ListenableFuture 类型的返回值,用于异步请求,使用WebAsyncManager 完成。

MapMethodProcessor:处理 Map 类型的返回值,将 Map 添加到 mavContainer 的Model 中。

ModelAndViewMethodReturnValueHandler:处理 ModelAndView 类型的返回值,将返回值中的 View 和 Model 设置到 mavContainer 中。

ModelAndViewResolverMethodRetumValueHandler:可以处理所有返回值,一般设置在最后一个,当别的处理器都不能处理时使用它处理。它内部封装了一个 List类型的 ModelAndViewResolver 和一个 annotationNotRequired 为 true 的 ModelAttributeMethodProcessor, ModelAndViewResolver 是一个将返回值解析为 ModelAndView 类型的通用接口,可以自定义后配置到 RequestMappingHandlerAdapter 中。处理返回值时先遍历所有的ModelAndViewResolver 进行处理,如果有可以处理的,则用它处理并将结果返回,如果都无法处理则调用 ModelAttributeMethodProcessor 进行处理。

ModelAttributeMethodProcessor :处理注释了 @ ModelAttribute 类型的返回值,如果annotationNotRequired true 还可以处理没有注释的非通用类型的返回值。ServletModelAttributeMethodProcessor:对返回值的处理同父类,这里只是修改了参数解析的功能,未对返回值处理功能做修改。

ModeIMethodProcessor:处理Model 类型返回值,将Model 中的值添加到 mavContainer 的Model 中。

ViewMethodReturnValueHandler:处理 View 类型返回值,如果返回值为空直接返回,否则将返回值设置到 mavContainer 的 View 中,并判断返回值是不是 redirect 类型,如果是则设置 mavContainer 的 redirectModelScenario true。

ViewNameMethodReturn ValueHandler:处理void 和String 类型返回值,如果返回值为空则直接返回,否则将返回值通过 mavContainer 的 setViewName 方法设置到其View 中,并判断返回值是不是redirect类型,如果是则设置mavContainer 的redirectModelScenario 为true。

2.3  处理器过程

那么接下来我们看下常用的处理器的处理过程。

2.3.1  RequestResponseBodyMethodProcessor

首当其冲的必须是我们现在常用的 RequestResponseBodyMethodProcessor,他的类图如下:

先看下他的支持的结果类型定义:

// 平时的 controller
@RequestMapping("/demo")
@RestController
public class DemoController {}
// RestController 注解的定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {}
// RequestResponseBodyMethodProcessor#supportsReturnType()
public boolean supportsReturnType(MethodParameter returnType) {
    // 方法返回类型所在的类 比如某个 Controller 类 含有@ResponseBody注解的
    // 或者返回类型上自己有 @ResponseBody注解的
    return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
}

现在前后端分离后,我们的 Controller 类上基本都是标的 @RestController 注解,而这个注解天然就带有 @ResponseBody 所以我们基本上大多的返回都是靠该处理器来做的处理。

那我们看下他的处理过程,首先我们进来的是处理结果的方法如下:

// RequestResponseBodyMethodProcessor#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    // 设置请求处理完成的标志
    mavContainer.setRequestHandled(true);
    // 输入输出消息 输入不是很清楚这里处理响应 为啥要有输出
    // 一个封装的是 HttpServletRequest 一个封装的是 HttpServletResponse
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
    // 通过处理器进行消息的写入
    // Try even with null return value. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

我们继续看父类的 writeWithMessageConverters 方法:

// AbstractMessageConverterMethodProcessor#writeWithMessageConverters
/**
 * Writes the given return type to the given output message.
 * @param value the value to write to the output message 返回值
 * @param returnType the type of the value 返回值参数对象 返回类型、所在类
 * @param inputMessage the input messages. Used to inspect the {@code Accept} header.
 * @param outputMessage the output message to write to
 * @throws IOException thrown in case of I/O errors
 * @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated
 * by the {@code Accept} header on the request cannot be met by the message converters
 * @throws HttpMessageNotWritableException thrown if a given message cannot
 * be written by a converter, or if the content-type chosen by the server
 * has no compatible converter.
 */
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    Object body;
    Class<?> valueType;
    Type targetType;
    // 字符串的处理
    if (value instanceof CharSequence) {
        body = value.toString();
        valueType = String.class;
        targetType = String.class;
    } else {
        body = value;
        // 返回值类型
        valueType = getReturnValueType(body, returnType);
        // 方法定义的返回类型
        targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
    }
    // Resource 类型的处理
    // ...
    MediaType selectedMediaType = null;
    MediaType contentType = outputMessage.getHeaders().getContentType();
    boolean isContentTypePreset = contentType != null && contentType.isConcrete();
    if (isContentTypePreset) {
        if (logger.isDebugEnabled()) {
            logger.debug("Found 'Content-Type:" + contentType + "' in response");
        }
        selectedMediaType = contentType;
    }
    else {
        HttpServletRequest request = inputMessage.getServletRequest();
        List<MediaType> acceptableTypes;
        try {
            // 获取当前请求支持接收的媒体类型
            acceptableTypes = getAcceptableMediaTypes(request);
        }
        catch (HttpMediaTypeNotAcceptableException ex) {
            int series = outputMessage.getServletResponse().getStatus() / 100;
            if (body == null || series == 4 || series == 5) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignoring error response content (if any). " + ex);
                }
                return;
            }
            throw ex;
        }
        // 获取当前解析器服务端支持的类型 这里的话有2个 application/json application/*+json
        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));
                }
            }
        }
        // 没匹配到进行报错
        if (mediaTypesToUse.isEmpty()) {
            if (logger.isDebugEnabled()) {
                logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
            }
            if (body != null) {
                throw new HttpMediaTypeNotAcceptableException(producibleTypes);
            }
            return;
        }
        // 排序
        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);
        }
    }
    // 最终会选择其中一个媒体类型进行返回处理 debug看出的是 application/json
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        // 遍历消息转换器 看看是否有支持的 
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            // json 默认是 MappingJackson2HttpMessageConverter
            // 他支持媒体类型 所有的或者json的 MediaType.ALL */* application/json 
            if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {
                // 执行通知器 进行增强 这里可以对返回值进行处理
                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) + "]");
                    addContentDispositionHeader(inputMessage, outputMessage);
                    // 处理器执行
                    if (genericConverter != null) {
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    } else {
                        ((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(getSupportedMediaTypes(body.getClass()));
    }
}

大致的主要的逻辑就是:

(1)决定当前返回值的类型、方法上定义的返回的类型,后续用于进行处理转换器的匹配

(2)获取当前请求允许接收的媒体类型以及当前处理器可返回的媒体类型,进行匹配

(3)没有匹配的直接报错返回

(4)有匹配的,遍历转换器进行处理,处理前先执行通知器的 beforeBodyWrite 留给用户进行自定义扩展

最后就是通过 jackson 的 objectWriter.writeValue(generator, value); 将返回值进行序列化。

关于结果返回的处理我们发现 spring 留了一个扩展点给我们更改 body 的口子,那我们看看下边的这个 getAdvice 的由来:

body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage);

getAdvice 是直接获取的父类的属性 advice 类型是 RequestResponseBodyAdviceChain的。

// AbstractMessageConverterMethodArgumentResolver#getAdvice()
// private final RequestResponseBodyAdviceChain advice;
RequestResponseBodyAdviceChain getAdvice() {
    return this.advice;
}

我这里就不一点点带大家看了,画了图直接看,他的起点也是来源于 RequestMappingHandlerAdapter适配器的 afterPropertySet 方法:

再结合下代码看下:

// getAdvice 返回 RequestResponseBodyAdviceChain
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                            (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                            inputMessage, outputMessage);

// 然后执行 RequestResponseBodyAdviceChain 的 beforeBodyWrite
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    // 调用内部的 processBody
    return processBody(body, returnType, contentType, converterType, request, response);
}
// RequestResponseBodyAdviceChain#processBody
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    // 遍历结果的增强器 寻找适配当前返回类型的 进行增强
    for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
        if (advice.supports(returnType, converterType)) {
            body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                    contentType, converterType, request, response);
        }
    }
    return body;
}

RequestResponseBodyMethodProcessor 就看到这里。 

3  小结

本节主要看了下结果处理器的概述,然后着重看了下平时最常用的 RequestResponseBodyMethodProcessor 处理器的过程,后续还会梳理一个更详细的图,比如我们常见的 404 的报错 500的报错都是哪些处理器处理的等都会梳理,有理解不对的地方还请指正哈。

posted @ 2025-06-25 23:11  酷酷-  阅读(38)  评论(0)    收藏  举报