关于spring:SpringMVCRequestMappingHandlerAdapter

74次阅读

共计 36127 个字符,预计需要花费 91 分钟才能阅读完成。

前言

在 SpringMVC-RequestMappingHandlerMapping 中对 web 申请到来时如何获取解决该申请的 handler 进行了剖析,那么获取到 web 申请对应的 handler 之后,SpringMVC 框架还会依据 handler 获取其对应的 HandlerAdapter,handler 的理论执行就是由HandlerAdapter 实现,并且 HandlerAdapter 一共会做三件事件:入参解析 执行解决逻辑 返回值解决 。艰深的说就是执行办法前要把办法须要的参数筹备好,而后执行办法,最初办法执行后要对办法返回值进行解决。如果 handler 是由RequestMappingHandlerMapping 取得,那么执行该 handler 的 HandlerAdapter 应该为 RequestMappingHandlerAdapter,该篇文章将对RequestMappingHandlerAdapter入参解析 执行解决逻辑 返回值解决 三局部进行学习。

SpringBoot 版本:2.4.1

注释

一. RequestMappingHandlerAdapter 初始化

首先通过类图认识一下RequestMappingHandlerAdapter

由类图可知 RequestMappingHandlerAdapter 实现了 InitializingBean 接口,因而在容器加载 RequestMappingHandlerAdapter 时会执行其实现的 afterPropertiesSet() 办法。该办法如下所示。

public void afterPropertiesSet() {initControllerAdviceCache();

    if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

afterPropertiesSet()办法顺次为 RequestMappingHandlerAdapter 加载 ControllerAdviceBean 相干内容,加载参数解析器,加载返回后果处理器。其中参数解析器和返回后果处理器默认状况下会将 SpringMVC 框架提供的和用户自定义的一并进行加载,而加载 ControllerAdviceBean 相干内容理论就是先获取容器中所有由 @ControllerAdvice 注解润饰的 bean,而后将这些 bean 的由 @ModelAttribute 注解和 @InitBinder 注解润饰的办法加载到 RequestMappingHandlerAdapter 中,最初再判断由 @ControllerAdvice 注解润饰的 bean 是否实现了 RequestBodyAdviceResponseBodyAdvice接口,如果是,则将该 bean 也加载到 RequestMappingHandlerAdapter 中。initControllerAdviceCache()办法如下所示。

@ControllerAdvice 注解润饰的 bean 是性能增强型 Controller,通常搭配@ModelAttribute 注解和 @InitBinder 注解应用,能够实现:全局数据绑定 全局数据预处理 全局异样解决

private void initControllerAdviceCache() {if (getApplicationContext() == null) {return;}

    // 将容器中所有带 @ControllerAdvice 注解的 bean 获取进去并包装成 ControllerAdviceBean 对象
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

    // 遍历 ControllerAdviceBean 对象汇合
    for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {throw new IllegalStateException("Unresolvable type for ControllerAdviceBean:" + adviceBean);
        }
        // 获取 ControllerAdviceBean 对象(包含父对象)和其实现的接口的不禁 @RequestMapping 注解润饰且由 @ModelAttribute 注解润饰的办法汇合
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        // 获取 ControllerAdviceBean 对象(包含父对象)和其实现的接口的由 @InitBinder 注解润饰的办法汇合
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        // 判断 ControllerAdviceBean 对象是否实现了 RequestBodyAdvice 或 ResponseBodyAdvice 接口
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }

    if (!requestResponseBodyAdviceBeans.isEmpty()) {this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }

    if (logger.isDebugEnabled()) {int modelSize = this.modelAttributeAdviceCache.size();
        int binderSize = this.initBinderAdviceCache.size();
        int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
        int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
        if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {logger.debug("ControllerAdvice beans: none");
        }
        else {
            logger.debug("ControllerAdvice beans:" + modelSize + "@ModelAttribute," + binderSize +
                    "@InitBinder," + reqCount + "RequestBodyAdvice," + resCount + "ResponseBodyAdvice");
        }
    }
}

大节:RequestMappingHandlerAdapter初始化时会将解析参数,参数预处理,执行后果解决等步骤须要用到的 bean 进行加载。

二. RequestMappingHandlerAdapter 参数解析

先通过 DispatcherServlet 中的 doDispatch() 办法察看 handler 执行的入口。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {noHandlerFound(processedRequest, response);
                return;
            }

            // 依据 handler 获取 HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 如果是 GET 或者 HEAD 申请,则获取 LastModified 工夫戳并判断申请的资源是否扭转
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}
            }

            // 执行拦截器的 preHandler 办法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}

            // 执行 handler
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {return;}

            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {......}
        catch (Throwable err) {......}
        ......
    }
    catch (Exception ex) {......}
    catch (Throwable err) {......}
    finally {......}
}

handler 的执行产生在 HandlerAdapter 接口申明的 handle() 办法中,由后面类图可知,RequestMappingHandlerAdapter继承于抽象类 AbstractHandlerMethodAdapter,该抽象类实现了HandlerAdapter 接口,并在其实现的 handle() 办法中调用了其申明的形象办法 handleInternal(),因而 handler 的执行入口是RequestMappingHandlerAdapterhandleInternal()办法,该办法如下所示。

protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    // 如果对反对的 HTTP 申请办法和会话进行了设置,则校验 request 是否合乎设置,校验失败抛出异样
    checkRequest(request);

    // 如果 synchronizeOnSession 为 true 且能从 request 获取会话对象,则以同步形式执行 handler
    if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);
        if (session != null) {Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // 以不同步形式执行 handler
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    //response 如果没有蕴含 Cache-Control 响应头,则在这里为 response 增加 Cache-Control 响应头
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        //handler 如果由 @SessionAttributes 注解润饰且申明了会话属性,则将 response 响应头的 Cache-Control 设置为 no-store
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            // 否则依据 WebContentGenerator 的设置来丰盛 response
            prepareResponse(response);
        }
    }

    return mav;
}

持续看 invokeHandlerMethod() 的实现。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // 获取 WebDataBinderFactory,顾名思义,该工厂对象用于获取 WebDataBinder,以实现 request 参数与 JavaBean 的绑定
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        // 获取 ModelFactory,用于在 handler 执行前帮助初始化 Model,并在执行后更新 Model
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        //InvocableHandlerMethod 继承于 HandlerMethod 并对其进行了扩大,可能应用参数解析器从 request 中获取参数并将获取到的参数作为入参调用 handler 办法
        //ServletInvocableHandlerMethod 继承于 InvocableHandlerMethod 并对其进行了扩大,可能应用返回值处理器解决 handler 办法的返回值以及基于 @ResponseStatus 注解设置响应状态
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        // 为 invocableMethod 设置参数解析器
        if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        // 为 invocableMethod 设置返回值处理器
        if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // 为 invocableMethod 设置 WebDataBinderFactory 和 ModelFactory
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // 创立 ModelAndViewContainer 对象
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        // 从 request 中获取 Input Flash Attributes 并将其加载到 ModelAndViewContainer 应用的模型 Model 中
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        // 初始化模型 Model
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        // 设置是否在重定向时不应用默认模型 defaultModel,默认值为 false,即重定向时也要应用 defaultModel
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 执行 handler
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {return null;}

        // 更新模型 Model,而后创立 ModelAndView 对象并返回
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {webRequest.requestCompleted();
    }
}

invokeHandlerMethod()办法有点长,所做的事件能够概括为首先为执行 handler 筹备各种必要条件(参数解析器,返回值处理器等),而后执行 handler,最初更新模型 Model 并生成 ModelAndView 对象。这里特地阐明一下下面源码中的 ModelAndViewContainer 对象,该对象的源码的正文翻译如下。

记录与模型 Model 和视图 View 相干的由 HandlerMethodArgumentResolversHandlerMethodReturnValueHandlers在 handler 办法执行中做出的相干决策。

要了解下面的话,先看一下 ModelAndViewContainer 的成员变量。

public class ModelAndViewContainer {

    // 重定向时不应用 defaultModel 标记位,默认为 false(示意重定向时也要应用 defaultModel)private boolean ignoreDefaultModelOnRedirect = false;

    // 视图
    @Nullable
    private Object view;

    // 默认模型
    private final ModelMap defaultModel = new BindingAwareModelMap();

    // 重定向模型,重定向场景时能够应用该模型
    @Nullable
    private ModelMap redirectModel;

    // 重定向场景的标记位,示意 handler 是否返回重定向指令
    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);

    // 标识会话解决是否实现
    private final SessionStatus sessionStatus = new SimpleSessionStatus();

    // 标识 handler 是否执行实现
    private boolean requestHandled = false;
    
}

ModelAndViewContainer 次要性能如下。

  • 保护模型Model:非重定向场景下应用 defaultModel,重定向场景下且 ignoreDefaultModelOnRedirect 为 true 时应用 redirectModel;
  • 保护视图View:如果是字符串类型则是逻辑视图;
  • 保护是否是重定向场景:通过 redirectModelScenario 属性字段进行标识;
  • 保护 handler 是否执行完:通过 requestHandled 属性字段进行标识。

因为在前面参数解析和返回后果解决的过程中会将 ModelAndViewContainer 对象传递来传递去,其实这个对象做的事件大部分是存储数据和记录状态以不便最初获取 ModelAndView 对象,所以在这里先对其进行简要阐明。

回到 invokeHandlerMethod() 办法,handler 的理论执行是产生在 ServletInvocableHandlerMethod 对象的 invokeAndHandle() 办法中,持续看这个办法的实现。

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 参数解析,执行 handler
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    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;
    }

    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 返回值解决
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

invokeAndHandle()办法中,调用了 ServletInvocableHandlerMethod 的父类 InvocableHandlerMethodinvokeForRequest()来实现参数解析和执行 handler。其实现如下。

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 参数解析
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {logger.trace("Arguments:" + Arrays.toString(args));
    }
    // 执行 handler
    return doInvoke(args);
}

到这里,终于看到了参数解析和执行 handler 的办法。本大节次要学习参数解析过程,本大节注释开始。

getMethodArgumentValues()办法是 InvocableHandlerMethod 提供的办法,该办法实现两个事件:

  • 获取须要执行的 handler 办法的参数信息,包含:参数名,参数类型,参数注解(如果有的话)等,失去的是一个 MethodParameter[] 数组,每一个 handler 办法的参数信息都会封装成一个 MethodParameter 对象并退出到 MethodParameter[] 数组中;
  • 将 handler 办法的参数信息与 request 送入参数解析器,通过参数解析器将须要的参数从 request 中获取进去。

getMethodArgumentValues()的实现如下所示。

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 获取须要执行的 handler 办法的参数信息
    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}

    Object[] args = new Object[parameters.length];
    // 遍历所有 handler 办法的参数信息
    for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {continue;}
        // 遍历所有参数解析器,寻找可能解析以后 handler 办法参数的参数解析器,并将其缓存
        if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            // 将以后 handler 办法参数信息与 request 送入参数解析器,通过参数解析器将须要的参数从 request 中获取进去
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        catch (Exception ex) {if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();
                if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));
                }
            }
            throw ex;
        }
    }
    return args;
}

InvocableHandlerMethod的 resolvers 成员变量是一个 HandlerMethodArgumentResolverComposite 对象,该对象和其它参数解析器一样实现了 HandlerMethodArgumentResolver 接口,上面看一下其实现的 resolveArgument() 办法。

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

    // 获取 handler 办法参数对应的参数解析器(个别这里不必遍历全副参数解析器,能够间接从缓存中获取)HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    // 调用参数解析器从 request 中获取参数
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

在下面 resolveArgument() 办法中调用了参数解析器的 resolveArgument() 办法,而不同参数解析器的 resolveArgument() 办法实现存在肯定差异。

在日常的 web 开发中,应用 @RequestParam@PathVariable@RequestBody 注解较多,不同注解润饰的参数对应的参数解析器不尽相同,首先看一下这三种注解对应的参数解析器的类图。

由类图能够晓得,@RequestParam注解和 @PathVariable 注解对应的参数解析器继承于 AbstractNamedValueMethodArgumentResolver 抽象类,该抽象类实现了 HandlerMethodArgumentResolver 接口,因而这两个注解对应的参数解析器调用 resolveArgument() 办法理论是调用其父类实现的 resolveArgument() 办法。而 @RequestBody 注解对应的参数解析器 RequestResponseBodyMethodProcessor 对其父类实现的 resolveArgument() 办法进行了重写,故该注解对应的参数解析器调用 resolveArgument() 办法时理论是调用其自身重写的办法。

先看一下 AbstractNamedValueMethodArgumentResolverresolveArgument()办法的实现。

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

    // 依据 handler 办法的参数信息创立 NamedValueInfo 对象
    // 例如 @RequestParam(name = "password", required = true, defaultValue = "admin") String pw
    // 创立的 NamedValueInfo 对象为 NamedValueInfo(name=password, required=true, defaultValue=admin)
    NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    MethodParameter nestedParameter = parameter.nestedIfOptional();

    Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
    if (resolvedName == null) {
        throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");
    }

    // 从 request 中解析获取参数 arg
    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    if (arg == null) {if (namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
        }
        else if (namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
        arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    }
    else if ("".equals(arg) && namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
    }

    if (binderFactory != null) {
        // 创立 WebDataBinder,并执行由 @InitBinder 注解润饰的办法来初始化 WebDataBinder
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            // 应用 WebDataBinder 实现 arg 参数的类型转换和绑定
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
        catch (ConversionNotSupportedException ex) {throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        catch (TypeMismatchException ex) {throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        if (arg == null && namedValueInfo.defaultValue == null &&
                namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
    }

    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

    return arg;
}

下面源码中的 resolveName() 办法是 AbstractNamedValueMethodArgumentResolver 申明的一个形象办法,@RequestParam注解和 @PathVariable 注解对应的参数解析器均对其进行了实现,具体的实现逻辑比较简单,这里不再赘述,总之该办法能够从 request 中获取指定名称的参数。参数获取到之后,还会通过 WebDataBinderFactory 创立 WebDataBinder 对象,在创立 WebDataBinder 对象的同时还会调用所有由 @InitBinder 注解润饰的办法来初始化 WebDataBinder 对象,最初应用 WebDataBinder 来对刚刚从 request 中获取到的参数进行类型转换和绑定(即先将 arg 参数依照咱们的预期进行类型转换,而后再将 arg 指向转换后的值)。对于 WebDataBinder 如何实现类型转换和绑定,将会在前面的一篇文章中专门进行学习,这里不再进行深刻。

上面再看一下 RequestResponseBodyMethodProcessorresolveArgument()办法的实现。

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();
    // 从 request 中获取参数并调用音讯转换器将参数转换为冀望的类型
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);

    if (binderFactory != null) {
        // 创立 WebDataBinder,并执行由 @InitBinder 注解润饰的办法来初始化 WebDataBinder
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        if (arg != null) {
            // 如果 handler 办法的参数由 @Valid 或 @Validated 注解润饰,则对刚刚获取到的参数进行参数校验
            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());
        }
    }

    return adaptArgumentIfNecessary(arg, parameter);
}

readWithMessageConverters()办法理论就是调用 SpringMVC 框架提供的(或用户实现的)音讯转换器来实现申请体到参数对象的绑定,并且如果应用了 @Valid@Validated注解来进行参数校验,则会在 readWithMessageConverters() 办法获取到参数对象后创立 WebDataBinder 来实现参数校验,如果校验不通过则会抛出 MethodArgumentNotValidException 全局异样,通过解决该异样能够对参数校验不过的状况进行对立解决。上面再看一下 readWithMessageConverters() 的实现。

protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
        Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    Assert.state(servletRequest != null, "No HttpServletRequest");
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
    
    // 调用父类的 readWithMessageConverters()办法
    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    if (arg == null && checkRequired(parameter)) {
        throw new HttpMessageNotReadableException("Required request body is missing:" +
                parameter.getExecutable().toGenericString(), inputMessage);
    }
    return arg;
}

RequestResponseBodyMethodProcessorreadWithMessageConverters()办法中调用了其父类 AbstractMessageConverterMethodArgumentResolverreadWithMessageConverters()办法,其实现如下所示。

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
        Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    MediaType contentType;
    boolean noContentType = false;
    try {
        // 获取媒体类型,例如 application/json;charset=UTF-8
        contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }

    // 获取 handler 的 Class 对象
    Class<?> contextClass = parameter.getContainingClass();
    //targetClass 示意 handler 办法参数类型的 Class 对象
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();}

    // 获取 http 申请办法,例如 POST
    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;

    //EmptyBodyCheckingHttpInputMessage 实现了 HttpInputMessage 接口,加强提供了一个 hasBody()办法用于判断 request 是否有申请体
    EmptyBodyCheckingHttpInputMessage message;
    try {message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

        // 循环遍历音讯转换器
        for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter =
                    (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            // 判断音讯转换器是否能够解决以后申请
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                    (targetClass != null && converter.canRead(targetClass, contentType))) {if (message.hasBody()) {// 在音讯转换器读取申请体前调用所有 RequestBodyAdvice 接口的 beforeBodyRead()办法,以实现一些定制操作
                    HttpInputMessage msgToUse =
                            getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    // 音讯转换器读取申请体并将其转换为参数对象
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                            ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    // 在音讯转换器实现转换后调用所有 RequestBodyAdvice 接口的 afterBodyRead()办法,以实现一些定制操作
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                }
                else {body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    }
    catch (IOException ex) {throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }

    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 +"]";
    });

    return body;
}

AbstractMessageConverterMethodArgumentResolverreadWithMessageConverters()办法中,会循环遍历所有音讯转换器并判断其是否能够解决以后申请,同一个申请只会有一个音讯转换器能够解决。音讯转换器读取申请体前,会先获取所有适配以后 handler 的 RequestBodyAdvice 接口,而后循环遍历这些接口并执行其 beforeBodyRead() 办法,以实现一些定制解决,同理在音讯转换器实现转换后还会循环遍历这些接口并执行其 afterBodyRead() 办法,这里应用了 Spring 的 AOP:如果是由@ControllerAdvice 注解润饰的 RequestBodyAdvice 接口(切面),那么会依据 @ControllerAdvice 注解的配置信息来判断该切片是否适配以后 handler,如果是不禁 @ControllerAdvice 注解润饰的 RequestBodyAdvice 接口(切面),则直接判断该切片适配以后 handler。

至此,从 request 中解析 @RequestParam@PathVariable@RequestBody注解润饰的参数的流程根本剖析结束。

大节:RequestMappingHandlerAdapter在解析参数前会依据须要执行的 handler 办法生成一个 ServletInvocableHandlerMethod 对象,而后为该对象加载参数解析器,在进行参数解析时,会遍历所有加载的参数解析器并判断遍历到的参数解析器是否解析以后参数,如果能,则应用该参数解析器实现参数解析。

三. RequestMappingHandlerAdapter 执行 handler 办法

在上一大节中曾经晓得,handler 办法的参数获取和执行产生在 InvocableHandlerMethodinvokeForRequest()办法中,再给出该办法的源码如下所示。

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 解析参数
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {logger.trace("Arguments:" + Arrays.toString(args));
    }
    // 执行 handler
    return doInvoke(args);
}

上一大节次要是学习 getMethodArgumentValues() 办法做了什么事件,本大节开始学习 doInvoke() 办法如何执行 handler。doInvoke()办法实现如下所示。

protected Object doInvoke(Object... args) throws Exception {
    // 如果 handler 办法是桥接办法则获取其桥接办法,否则获取其自身
    Method method = getBridgedMethod();
    ReflectionUtils.makeAccessible(method);
    try {if (KotlinDetector.isSuspendingFunction(method)) {return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
        }
        // 反射形式调用 handler 办法,getBean()获取 handler 对象,args 是解析失去的参数
        return method.invoke(getBean(), args);
    }
    catch (IllegalArgumentException ex) {assertTargetBean(method, getBean(), args);
        String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
        throw new IllegalStateException(formatInvokeError(text, args), ex);
    }
    catch (InvocationTargetException ex) {
        // Unwrap for HandlerExceptionResolvers ...
        Throwable targetException = ex.getTargetException();
        if (targetException instanceof RuntimeException) {throw (RuntimeException) targetException;
        }
        else if (targetException instanceof Error) {throw (Error) targetException;
        }
        else if (targetException instanceof Exception) {throw (Exception) targetException;
        }
        else {throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
        }
    }
}

大节:handler 办法的执行是通过反射的形式实现的。

四. RequestMappingHandlerAdapter 解决返回后果

在第二大节中曾经晓得,返回值解决是产生在 ServletInvocableHandlerMethod 对象的 invokeAndHandle() 办法中,再给出其实现如下所示。

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 参数解析,执行 handler
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    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;
    }

    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 返回值解决
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

ServletInvocableHandlerMethod对象的成员变量 returnValueHandlers 是一个 HandlerMethodReturnValueHandlerComposite 对象,上面看一下该对象的 handleReturnValue() 办法。

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    
    // 依据返回值类型匹配返回值处理器
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {throw new IllegalArgumentException("Unknown return value type:" + returnType.getParameterType().getName());
    }
    // 返回值处理器对返回值进行解决
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

handleReturnValue() 办法中先是依据返回值类型匹配对应的返回值处理器,而后用匹配到的返回值处理器对返回值进行解决。基于 SpringMVC 框架进行 web 开发时,比拟规范的返回值类型应该为 ResponseEntity<T>,该类是由 Spring 提供的一个继承于HttpEntity<T> 的响应实体类,ResponseEntity<T>相较于 HttpEntity<T> 减少了响应状态码的成员变量,SpringMVC 框架专门提供了一个 ResponseEntity<T> 对应的返回值处理器 HttpEntityMethodProcessor。同时,如果返回值类型是个别类型或者自定义类型,然而 handler 办法或者 handler 由@ResponseBody 注解润饰,那么这种状况对应的返回值处理器是第二大节中呈现过的 RequestResponseBodyMethodProcessor。那么当初以HttpEntityMethodProcessorRequestResponseBodyMethodProcessor这两个返回值处理器为例,对返回值处理器如何解决返回值进行学习。

首先看一下这两个返回值处理器的类图。

HttpEntityMethodProcessorRequestResponseBodyMethodProcessor 均实现了 HandlerMethodReturnValueHandler 接口,该接口申明了一个用于判断返回值处理器是否能够解决以后返回值的办法 supportsReturnType()HttpEntityMethodProcessor 对该办法的实现如下。

public boolean supportsReturnType(MethodParameter returnType) {
    // 能够解决类型是 HttpEntity 且不是 RequestEntity 的返回值
    return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
            !RequestEntity.class.isAssignableFrom(returnType.getParameterType()));
}

RequestResponseBodyMethodProcessor对该办法的实现如下。

public boolean supportsReturnType(MethodParameter returnType) {
    // 如果返回值所在办法或类由 @ResponseBody 注解润饰,则能够解决
    return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
}

特地阐明一下,每次循环遍历返回值处理器时时,HttpEntityMethodProcessor会比 RequestResponseBodyMethodProcessor 先被遍历到,因而如果 handler 类或 handler 办法既由 @ResponseBody 注解润饰同时返回值类型还是 ResponseEntity,那么还是会应用HttpEntityMethodProcessor 来解决该返回值。

HandlerMethodReturnValueHandler接口还申明了一个办法为 handleReturnValue(),该办法会理论的执行解决返回值的逻辑。HttpEntityMethodProcessor 对该办法的实现如下。

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {mavContainer.setRequestHandled(true);
    if (returnValue == null) {return;}

    // 将 HttpServletRequest 封装成 ServletServerHttpRequest 对象
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    // 将 HttpServletResponse 封装成 ServletServerHttpResponse 对象
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    Assert.isInstanceOf(HttpEntity.class, returnValue);
    HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;

    HttpHeaders outputHeaders = outputMessage.getHeaders();
    HttpHeaders entityHeaders = responseEntity.getHeaders();
    if (!entityHeaders.isEmpty()) {
        // 设置响应头字段
        entityHeaders.forEach((key, value) -> {if (HttpHeaders.VARY.equals(key) && outputHeaders.containsKey(HttpHeaders.VARY)) {List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders);
                if (!values.isEmpty()) {outputHeaders.setVary(values);
                }
            }
            else {outputHeaders.put(key, value);
            }
        });
    }

    if (responseEntity instanceof ResponseEntity) {
        // 从 ResponseEntity 中获取响应码
        int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
        // 设置 HttpServletResponse 的响应码
        outputMessage.getServletResponse().setStatus(returnStatus);
        if (returnStatus == 200) {HttpMethod method = inputMessage.getMethod();
            // 如果响应码为 200,且 http 申请办法为 GET 或者 HEAD,且申请的资源没有扭转,此时立刻调用 HttpServletResponse 的 flushBuffer()办法将缓冲区的内容写入客户端
            if ((HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method))
                    && isResourceNotModified(inputMessage, outputMessage)) {outputMessage.flush();
                return;
            }
        }
        else if (returnStatus / 100 == 3) {String location = outputHeaders.getFirst("location");
            if (location != null) {
                // 如果是重定向场景,将 flash attributes 增加到 session 中
                saveFlashAttributes(mavContainer, webRequest, location);
            }
        }
    }

    // 调用音讯转换器将返回值写入响应
    writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage);

    outputMessage.flush();}

RequestResponseBodyMethodProcessor对该办法的实现如下。

public void handleReturnValue(@Nullable 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);
}

能够发现,HttpEntityMethodProcessorRequestResponseBodyMethodProcessorhandleReturnValue()办法中做了同样的事件:先将 ModelAndViewContainer 对象的 handler 办法是否执行标记设置为 true,而后基于 HttpServletRequestHttpServletResponse对象创立 ServletServerHttpRequestServletServerHttpResponse对象,最初调用公共父类 AbstractMessageConverterMethodProcessorwriteWithMessageConverters()办法解决返回值。其中 HttpEntityMethodProcessor 多做了两件事件:设置响应头和设置响应码。最初看一下 writeWithMessageConverters() 的实现。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    Object body;
    // 返回值类型的 Class 对象
    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());
    }

    // 判断返回值类型是否是 InputStreamResource 或者 Resource
    if (isResourceType(value, returnType)) {outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
        if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
                outputMessage.getServletResponse().getStatus() == 200) {Resource resource = (Resource) value;
            try {List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
                outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
                body = HttpRange.toResourceRegions(httpRanges, resource);
                valueType = body.getClass();
                targetType = RESOURCE_REGION_LIST_TYPE;
            }
            catch (IllegalArgumentException ex) {outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
                outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
            }
        }
    }

    //selectedMediaType 示意响应体的媒体类型
    MediaType selectedMediaType = null;
    // 从响应头中获取媒体类型,如果在 handler 办法中没有手动设置响应体的媒体类型那么这里的 contentType 为 null
    MediaType contentType = outputMessage.getHeaders().getContentType();
    boolean isContentTypePreset = contentType != null && contentType.isConcrete();
    // 如果在 handler 办法中设置了响应体媒体类型,则音讯转换器应用设置的媒体类型对返回值进行类型转换
    // 如果在 handler 办法中没有设置响应体媒体类型,则从申请中获取客户端承受的媒体类型并选取最合适的媒体类型
    if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug("Found'Content-Type:"+ contentType +"' in response");
        }
        selectedMediaType = contentType;
    }
    else {HttpServletRequest request = inputMessage.getServletRequest();
        List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
        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 (body != null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);
            }
            if (logger.isDebugEnabled()) {logger.debug("No match for" + acceptableTypes + ", supported:" + 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);
        }
    }

    if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();
        // 遍历所有音讯转换器
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                    (GenericHttpMessageConverter<?>) converter : null);
            // 判断音讯转换器是否反对转换以后返回值为冀望的媒体类型
            if (genericConverter != null ?
                    ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {// 在音讯转换器转换返回值前调用所有 ResponseBodyAdvice 接口的 beforeBodyWrite()办法,以实现一些定制操作
                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) + "]");
                    //Content-Disposition 响应头字段解决
                    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(this.allSupportedMediaTypes);
    }
}

writeWithMessageConverters() 办法中,次要是获取响应体媒体类型,而后调用音讯转换器将返回值依照冀望的媒体类型写入响应体,在写入前还会执行 ResponseBodyAdvice 切面对返回值实现一些定制操作。

大节:RequestMappingHandlerAdapter解决返回值首先会依据返回值类型匹配相应的返回值处理器来解决返回值,在返回值处理器中通常是先获取响应体的媒体类型,而后调用音讯转换器将返回值依照冀望的媒体类型写入响应体。

总结

RequestMappingHandlerAdapter是基于 SpringMVC 框架进行 web 开发应用频次最高的组件之一,RequestMappingHandlerAdapter在 handler 办法执行前会应用 SpringMVC 框架提供的和用户实现的参数解析器从 request 中获取 handler 办法须要的参数,而后基于反射形式调用 handler 办法,最初会应用 SpringMVC 框架提供的和用户实现的返回值处理器将 handler 办法的返回值写入响应体中。

正文完
 0