分享

精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

 小样样样样样样 2021-06-11

该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读

Spring 版本:5.1.14.RELEASE

HandlerAdapter 组件

HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器

由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:

HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

本文是接着《HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver》一文来分享 HandlerMethodReturnValueHandler 组件。在 HandlerAdapter 执行处理器的过程中,具体的执行过程交由 ServletInvocableHandlerMethod 对象来完成,其中需要先通过 HandlerMethodArgumentResolver 参数解析器从请求中解析出方法的入参,然后再通过反射机制调用对应的方法,获取到执行结果后需要通过 HandlerMethodReturnValueHandler 结果处理器来进行处理。

回顾

先来回顾一下 ServletInvocableHandlerMethod 在哪里调用返回值处理器的,可以回到 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》ServletInvocableHandlerMethod 小节下面的 invokeAndHandle 方法,如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // <1> 执行调用
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // <2> 设置响应状态码
    setResponseStatus(webRequest);

    // <3> 设置 ModelAndViewContainer 为请求已处理,返回,和 @ResponseStatus 注解相关
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    } else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    // <4> 设置 ModelAndViewContainer 为请求未处理
    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // <5> 处理返回值
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    } catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}
  • <5> 处调用 returnValueHandlers 对返回结果进行处理

  • returnValueHandlers 为 HandlerMethodReturnValueHandlerComposite 组合对象,包含了许多的结果处理器

HandlerMethodReturnValueHandler 接口

org.springframework.web.method.support.HandlerMethodReturnValueHandler,返回结果处理器

public interface HandlerMethodReturnValueHandler {

/**
 * 是否支持该类型
 */
boolean supportsReturnType(MethodParameter returnType);
/**
 * 处理返回值
 */
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

类图

因为返回结果类型是多变的,所以会有许多的 HandlerMethodReturnValueHandler 的实现类,上图仅列出了本文会分析的两个实现类

ModelAndViewContainer

org.springframework.web.method.support.ModelAndViewContainer,主要是作为 Model 和 View 的容器

构造方法

public class ModelAndViewContainer {
/**
 * 是否在 redirect 重定向时,忽略 {@link #redirectModel}
 */
private boolean ignoreDefaultModelOnRedirect = false;

/**
 * 视图,Object 类型。
 *
 * 实际情况下,也可以是 String 类型的逻辑视图
 */
@Nullable
private Object view;

/**
 * 默认使用的 Model 。实际上是个 Map
 */
private final ModelMap defaultModel = new BindingAwareModelMap();

/**
 * redirect 重定向的 Model ,在重定向时使用。
 */
@Nullable
private ModelMap redirectModel;

/**
 * 处理器返回 redirect 视图的标识
 */
private boolean redirectModelScenario = false;

/**
 * Http 响应状态
 */
@Nullable
private HttpStatus status;

private final Set<String> noBinding = new HashSet<>(4);

private final Set<String> bindingDisabled = new HashSet<>(4);

/**
 * 用于设置 SessionAttribute 的标识
 */
private final SessionStatus sessionStatus = new SimpleSessionStatus();

/**
 * 请求是否处理完的标识
 */
private boolean requestHandled = false;
}

getModel

getModel() 方法,获得 Model 对象。代码如下:

public ModelMap getModel() {
    // 是否使用默认 Model
    if (useDefaultModel()) {
        return this.defaultModel;
    }
    else {
        if (this.redirectModel == null) {
            this.redirectModel = new ModelMap();
        }
        return this.redirectModel;
    }
}

/**
 * Whether to use the default model or the redirect model.
 */
private boolean useDefaultModel() {
    return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
  • 从代码中,可以看出,有两种情况下,使用 defaultModel 默认 Model :

    • 情况一 !this.redirectModelScenario ,处理器返回 redirect 视图的标识为 false 的时候,即不重定向

    • 情况二 this.redirectModel == null && !this.ignoreDefaultModelOnRedirectredirectModel 重定向 Model 为,并且 ignoreDefaultModelOnRedirecttrue ,即忽略 defaultModel

  • 那么,问题就来了,redirectModelScenario 和 ignoreDefaultModelOnRedirect 什么时候被改变?

    • redirectModelScenario 属性,在下文的 ViewNameMethodReturnValueHandlerhandleReturnValue方法中会设置为true,详情见下文

    • ignoreDefaultModelOnRedirect 属性,和 RequestMappingHandlerAdapter 的 ignoreDefaultModelOnRedirect 的属性是一致的,默认为false

      在 RequestMappingHandlerAdapter 的 invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) 方法中,进行设置

  • 另外,org.springframework.ui.ModelMap 是继承 LinkedHashMap 类,并增加了部分常用方法,比较简单

View 相关的方法

public void setViewName(@Nullable String viewName) {
this.view = viewName;
}
@Nullable
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}

public void setView(@Nullable Object view) {
this.view = view;
}
@Nullable
public Object getView() {
return this.view;
}

public boolean isViewReference() {
return (this.view instanceof String);
}

requestHandled 属性

请求是否处理完的标识

关于 requestHandled 的修改地方,实际在 Spring MVC 地方蛮多处都可以进行修改,例如:

  • 在本文的开始处,ServletInvocableHandlerMethod 对象的 invokeAndHandle 方法中,会先设置为 false,表示请求还未处理,再交由 HandlerMethodReturnValueHandler 结果处理器去处理

  • 在后文的 RequestResponseBodyMethodProcessorhandleReturnValue 会设置为 true

处理完结果后,接下来 RequestMappingHandlerAdapter 需要通过 ModelAndViewContainer 获取 ModelAndView 对象,会用到 requestHandled 这个属性

// RequestMappingHandlerAdapter.java
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
        ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    modelFactory.updateModel(webRequest, mavContainer);
    // 情况一,如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。
    if (mavContainer.isRequestHandled()) {
        return null;
    }
    // 情况二,如果 mavContainer 未处理,则基于 `mavContainer` 生成 ModelAndView 对象
    ModelMap model = mavContainer.getModel();
    // 创建 ModelAndView 对象,并设置相关属性
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}

看到没,如果已处理,则返回的 ModelAndView 对象为 null

HandlerMethodReturnValueHandlerComposite

org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite,实现 HandlerMethodReturnValueHandler 接口,复合的 HandlerMethodReturnValueHandler 实现类

构造方法

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
/** HandlerMethodReturnValueHandler 数组 */
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
}

《HandlerAdapter 组件(一)之 HandlerAdapter》RequestMappingHandlerAdapter小节的 getDefaultReturnValueHandlers 方法中可以看到,默认的 returnValueHandlers 有哪些 HandlerMethodReturnValueHandler 实现类,注意这里是有顺序的添加哦

getReturnValueHandler

getReturnValueHandler(MethodParameter returnType) 方法,获得方法返回值对应的 HandlerMethodReturnValueHandler 对象,方法如下:

@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

很简单,遍历所有的 HandlerMethodReturnValueHandler 实现类,如果支持这个返回结果,则直接返回

这里为什么不加缓存呢?

supportsReturnType

supportsReturnType(MethodParameter returnType)方法,判断是否支持该返回类型,方法如下:

@Override
public boolean supportsReturnType(MethodParameter returnType) {
    return getReturnValueHandler(returnType) != null;
}

实际上就是调用 getReturnValueHandler(MethodParameter returnType) 方法,存在对应的 HandlerMethodReturnValueHandler 实现类表示支持

handleReturnValue

handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)方法,处理返回值,方法如下:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    // <x> 获得 HandlerMethodReturnValueHandler 对象
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

这里好奇的是没有调用 getReturnValueHandler(MethodParameter returnType)方法获取对应的 HandlerMethodReturnValueHandler 对象,而是调用 selectHandler(Object value, MethodParameter returnType) 方法,方法如下:

@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    // 判断是否为异步返回值
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    // 遍历 HandlerMethodReturnValueHandler 数组,逐个判断是否支持
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        // 如果支持,则返回
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (handler instanceof AsyncHandlerMethodReturnValueHandler &&
                ((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
            return true;
        }
    }
    return false;
}

getReturnValueHandler(MethodParameter returnType) 的基础上,增加了异步处理器 AsyncHandlerMethodReturnValueHandler 的判断

【重点】RequestResponseBodyMethodProcessor

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor,继承 AbstractMessageConverterMethodProcessor 抽象类,处理方法参数添加了 @RequestBody 注解方法入参,或者处理方法添加了 @ResponseBody 注解的返回值。

因为前后端分离之后,后端基本是提供 Restful API ,所以 RequestResponseBodyMethodProcessor 成为了目前最常用的 HandlerMethodReturnValueHandler 实现类。

从图中,我们也会发现,RequestResponseBodyMethodProcessor 也是 HandlerMethodArgumentResolver 的实现类。示例代码:

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/walks")
    public List<User> walk(@RequestBody User user) {
        List<User> users = new ArrayList();
        users.add(new User().setUsername("nihao"));
        users.add(new User().setUsername("zaijian"));
        return users;
    }
}

虽然,walks() 方法的返回值没添加 @ResponseBody 注解,但是 @RestController 注解,默认有 @ResponseBody 注解

构造方法

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
super(converters);
}

public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager) {

super(converters, manager);
}

public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable List<Object> requestResponseBodyAdvice) {

super(converters, null, requestResponseBodyAdvice);
}

public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager, @Nullable List<Object> requestResponseBodyAdvice) {

super(converters, manager, requestResponseBodyAdvice);
}
}
  • converters 参数,HttpMessageConverter 数组。关于 HttpMessageConverter,就是将返回结果设置到响应中,供客户端获取。例如,我们想要将 POJO 对象,返回成 JSON 数据给前端,就会使用到 MappingJackson2HttpMessageConverter 类。

  • requestResponseBodyAdvice 参数,在父类 AbstractMessageConverterMethodArgumentResolver 中会将其转换成 RequestResponseBodyAdviceChain 对象 advice,不知你是否还记得这个参数,来回顾一下:

    《HandlerAdapter 组件(一)之 HandlerAdapter》RequestMappingHandlerAdapter1.afterPropertiesSet 初始化方法中,第一步就会初始化所有 ControllerAdvice 相关的类

    然后在1.4 getDefaultReturnValueHandlers方法中,创建 RequestResponseBodyMethodProcessor 处理器时,会传入 requestResponseBodyAdvice 参数

    使用示例可以参考 SpringMVC 中 @ControllerAdvice 注解的三种使用场景

supportsParameter

实现 supportsParameter(MethodParameter returnType) 方法,判断是否支持处理该方法参数,方法如下:

@Override
public boolean supportsParameter(MethodParameter parameter) {
    // 该参数是否有 @RequestBody 注解
    return parameter.hasParameterAnnotation(RequestBody.class);
}

该方法参数是否有 @RequestBody 注解

resolveArgument

实现 resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) 方法,从请求中解析出带有 @RequestBody 注解的参数,方法如下:

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();
    // 从请求体中解析出方法入参对象
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);

    // 数据绑定相关
    if (binderFactory != null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        if (arg != null) {
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        if (mavContainer != null) {
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
        }
    }
    // 返回方法入参对象,如果有必要,则通过 Optional 获取对应的方法入参
    return adaptArgumentIfNecessary(arg, parameter);
}

调用readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType)方法,从请求体中解析出方法入参对象

【核心】readWithMessageConverters

从请求体中解析出方法入参,方法如下:

@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
        Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    // <1> 创建 ServletServerHttpRequest 请求对象
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    Assert.state(servletRequest != null, "No HttpServletRequest");
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

    // <2> 读取请求体中的消息并转换成入参对象
    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    // <3> 校验方法入参对象
    if (arg == null && checkRequired(parameter)) {
        throw new HttpMessageNotReadableException("Required request body is missing: " +
                parameter.getExecutable().toGenericString(), inputMessage);
    }
    return arg;
}
// AbstractMessageConverterMethodArgumentResolver.java
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
        Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    // <1> 获取使用的 MediaType 对象
    MediaType contentType;
    boolean noContentType = false;
    try {
        // <1.1> 从请求头中获取 "Content-Type"
        contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
        noContentType = true;
        // <1.2> 为空则默认为 application/octet-stream
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }

    // <2> 获取方法参数的 containing class 和 目标类型,用于 HttpMessageConverter 解析
    Class<?> contextClass = parameter.getContainingClass();
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        // 如果为空,则从方法参数中解析出来
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }

    // <3> 获取 HTTP 方法
    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;

    // <4> 开始从请求中解析方法入参
    EmptyBodyCheckingHttpInputMessage message;
    try {
        // <4.1> 将请求消息对象封装成 EmptyBodyCheckingHttpInputMessage,用于校验是否有请求体,没有的话设置为 `null`
        message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

        // <4.2> 遍历 HttpMessageConverter
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? 
                                                               (GenericHttpMessageConverter<?>) converter : null);
            // 如果该 HttpMessageConverter 能够读取当前请求体解析出方法入参
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) : 
                (targetClass != null && converter.canRead(targetClass, contentType))) {
                // <4.2.1> 如果请求体不为空
                if (message.hasBody()) {
                    HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    // 通过该 HttpMessageConverter 从请求体中解析出方法入参对象
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : 
                            ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    // 调用 RequestResponseBodyAdvice 的 afterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                }
                // <4.2.2> 如果请求体为空,则无需解析请求体
                else {
                    // 调用 RequestResponseBodyAdvice 的 afterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改
                    body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }

    // <5> 校验解析出来的方法入参对象是否为空
    if (body == NO_VALUE) {
        if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                (noContentType && !message.hasBody())) {
            return null;
        }
        throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
    }

    // 打印日志
    MediaType selectedContentType = contentType;
    Object theBody = body;
    LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
        return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
    });

    // <6> 返回方法入参对象
    return body;
}

我们直接看到父类 AbstractMessageConverterMethodArgumentResolver 的 readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType)这个核心方法,大致逻辑如下:

  1. 获取使用的 MediaType 对象 contentType

    1. 从请求头中获取 Content-Type

    2. 请求头中没有则设置为默认的类型 application/octet-stream

  2. 获取方法参数的 containing classtargetClass 目标类型,用于 HttpMessageConverter 解析

  3. 获取 HTTP 方法

  4. 开始从请求中解析方法入参Object body

    1. 如果请求体不为空,则通过该 HttpMessageConverter 从请求体中解析出方法入参对象

    2. 如果请求体为空,则无需解析请求体 

    1. 将请求消息对象封装成 EmptyBodyCheckingHttpInputMessage,用于校验是否有请求体,没有的话设置为 null

    2. 遍历所有的 HttpMessageConverter 实现类,调用其 canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType)方法,判断当前 HttpMessageConverter 实现类是否支持解析该方法入参,如果返回 true,则使用该 HttpMessageConverter 实现类进行解析

      注意:上面不管请求体是否为空,都会调用 RequestResponseBodyAdviceafterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改

  5. 校验解析出来的方法入参对象是否为空,抛出异常或者返回null

  6. 返回方法入参对象body


方法虽然很长,但是不难理解,大致逻辑就是找到合适的 HttpMessageConverter 实现类从请求体中获取到方法入参对象

逻辑和下面的 writeWithMessageConverters 差不多,我们重点来看到下面这个方法

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多