Spring 源碼解析 @RequestBody @ResponseBody 的來(lái)龍去脈

@RequestBody 和 @ResponseBody 是實(shí)際開(kāi)發(fā)中很常用的兩個(gè)注解,通常用來(lái)解析和響應(yīng)JSON,用起來(lái)十分的方便,這兩個(gè)注解的背后是如何實(shí)現(xiàn)的?

源碼版本

SpringBoot 2.1.3.RELEASE

RequestResponseBodyMethodProcessor

Resolves method arguments annotated with @RequestBody and handles return values from methods annotated with @ResponseBody by reading and writing to the body of the request or response with an HttpMessageConverter.
An @RequestBody method argument is also validated if it is annotated with @javax.validation.Valid. In case of validation failure, MethodArgumentNotValidException is raised and results in an HTTP 400 response status code if DefaultHandlerExceptionResolver is configured.

簡(jiǎn)單來(lái)說(shuō),這個(gè)類(lèi)用來(lái)解析@RequestBody的參數(shù)和處理 @ResponseBody返回值,通過(guò) HttpMessageConverter 這個(gè)接口來(lái)實(shí)現(xiàn)。

如果@RequestBody標(biāo)記的參數(shù)包含@Valid,還會(huì)對(duì)這個(gè)參數(shù)進(jìn)行校驗(yàn)。

繼承關(guān)系

image.png

HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 分別是Spring的參數(shù)處理器和返回值處理器

  • HandlerMethodArgumentResolver
public interface HandlerMethodArgumentResolver {

    boolean supportsParameter(MethodParameter parameter);

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

}

Spring的參數(shù)解析器接口,supportsParameter() 方法用于判斷解析器是否支持當(dāng)前Controller方法的參數(shù),resolveArgument() 則是將Request解析為Controller方法對(duì)應(yīng)的參數(shù)Bean

  • HandlerMethodReturnValueHandler
public interface HandlerMethodReturnValueHandler {

    boolean supportsReturnType(MethodParameter returnType);

    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

同理這個(gè)接口將Controller方法返回的對(duì)象,封裝為Response

我們?cè)趯?shí)際開(kāi)發(fā)時(shí),也可以實(shí)現(xiàn)這兩個(gè)接口自定義自己的參數(shù)解析和響應(yīng)處理,RequestResponseBodyMethodProcessor 實(shí)現(xiàn)了這兩個(gè)接口,既做了參數(shù)解析器也做了響應(yīng)處理器。

RequestResponseBodyMethodProcessor 源碼分析

我們來(lái)看一下 RequestResponseBodyMethodProcessor 是如何工作的,以解析參數(shù)為例

  • resolveArgument
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 支持標(biāo)記@RequestBody的參數(shù)
        return parameter.hasParameterAnnotation(RequestBody.class);
    }
    
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        parameter = parameter.nestedIfOptional();
        // 通過(guò)HttpMessageConverters 將請(qǐng)求體, 封裝為@RequestBody所標(biāo)記的XXBean
        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) {
                // 如果存在@Valid 對(duì)參數(shù)進(jìn)行校驗(yàn)
                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);
    }
    
    @Override
    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);

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

作為參數(shù)解析器,RequestResponseBodyMethodProcessor 支持所有標(biāo)記@RequestBody的參數(shù)。在resolveArgument()方法中,通過(guò)調(diào)用readWithMessageConverters() 將 Request 轉(zhuǎn)為對(duì)應(yīng) arg。我們來(lái)看一下 readWithMessageConverters() 到底做了什么

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

        // 當(dāng)前請(qǐng)求的contentType
        MediaType contentType;
        boolean noContentType = false;
        try {
            contentType = inputMessage.getHeaders().getContentType();
        }
        catch (InvalidMediaTypeException ex) {
            throw new HttpMediaTypeNotSupportedException(ex.getMessage());
        }
        if (contentType == null) {
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
        }

        // Controller參數(shù)的Class
        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();
        }
        
        // 當(dāng)前請(qǐng)求方式
        HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
        Object body = NO_VALUE;

        EmptyBodyCheckingHttpInputMessage message;
        try {
            message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
            
            // 遍歷所有的HttpMessageConverter,
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                GenericHttpMessageConverter<?> genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                        
                // 如果當(dāng)前的HttpMessageConverter可以解析對(duì)應(yīng)的 class和contentType
                if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                        (targetClass != null && converter.canRead(targetClass, contentType))) {
                    if (message.hasBody()) {
                        HttpInputMessage msgToUse =
                                getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                                
                        // 將Http報(bào)文轉(zhuǎn)換為對(duì)應(yīng)的class
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                                
                        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;
    }

上述代碼核心邏輯就是遍歷當(dāng)前解析中配置的所有 HttpMessageConverter,如果某個(gè)Converter可以解析當(dāng)前的 contentType,就把轉(zhuǎn)換工作交給他去進(jìn)行。

之前做過(guò)將默認(rèn)解析替換為fastjson,當(dāng)時(shí)就是添加一個(gè)FastJson實(shí)現(xiàn)的HttpMessageConverter,但是那時(shí)候并不理解這么做是為了什么,現(xiàn)在才恍然大悟...

  • handleReturnValue

RequestResponseBodyMethodProcessor 的Response處理邏輯和解析邏輯類(lèi)似,找到一個(gè)支持的HttpMessageConverter,把響應(yīng)工作交給他,感興趣的童鞋可以自己找下源碼。

RequestResponseBodyMethodProcessor 是怎么被調(diào)用的

上面講了 RequestResponseBodyMethodProcessor 做了參數(shù)解析和響應(yīng)處理的工作,那么他在Spring框架中是怎么被調(diào)用的,我們來(lái)看一下

image.png

如圖,RequestMappingHandlerAdapter 的resolvers(Request解析器)、handlers(Response處理器)還有 ExceptionHandlerExceptionResolver 的handlers 調(diào)用了 RequestResponseBodyMethodProcessor

RequestMappingHandlerAdapter

我們只分析一下 RequestMappingHandlerAdapter ,該類(lèi)對(duì)所有標(biāo)記 @RequestMapping的注解進(jìn)行解析和響應(yīng)

在WebMvcConfigurationSupport中,配置了該Bean,將其加入到Spring容器中,我們自定義的參數(shù)解析、響應(yīng)解析、和HttpMessageConvert 通過(guò)上圖的方法set到 RequestMappingHandlerAdapter 中。

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
        adapter.setContentNegotiationManager(mvcContentNegotiationManager());
        // 獲取所有HttpMessageConverter,包括我們自定義的配置
        adapter.setMessageConverters(getMessageConverters());
        adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
        // 自定義的參數(shù)解析器
        adapter.setCustomArgumentResolvers(getArgumentResolvers());
        // 自定義的響應(yīng)處理器
        adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

        if (jackson2Present) {
            adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
            adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
        }

        AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
        configureAsyncSupport(configurer);
        if (configurer.getTaskExecutor() != null) {
            adapter.setTaskExecutor(configurer.getTaskExecutor());
        }
        if (configurer.getTimeout() != null) {
            adapter.setAsyncRequestTimeout(configurer.getTimeout());
        }
        adapter.setCallableInterceptors(configurer.getCallableInterceptors());
        adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

        return adapter;
    }

繼續(xù)說(shuō) RequestMappingHandlerAdapter ,getDefaultArgumentResolvers() 封裝了SpringBoot中的默認(rèn)參數(shù)解析器,其中就有我們的本節(jié)所講的 RequestResponseBodyMethodProcessor ,在afterPropertiesSet() 方法中調(diào)用了該方法

    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        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);
        }
    }
    
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        
        // 添加RequestResponseBodyMethodProcessor
        resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        
        // 省略,詳見(jiàn)源碼...

        return resolvers;
    }
    
    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

        // Single-purpose return value types
        handlers.add(new ModelAndViewMethodReturnValueHandler());
        handlers.add(new ModelMethodProcessor());
        handlers.add(new ViewMethodReturnValueHandler());
        handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
                this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
        handlers.add(new StreamingResponseBodyReturnValueHandler());
        handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));
        handlers.add(new HttpHeadersReturnValueHandler());
        handlers.add(new CallableMethodReturnValueHandler());
        handlers.add(new DeferredResultMethodReturnValueHandler());
        handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

        // Annotation-based return value types
        handlers.add(new ModelAttributeMethodProcessor(false));
        
        // 添加RequestResponseBodyMethodProcessor
        handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));

        // 省略,詳見(jiàn)源碼...

        return handlers;
    }
RequestResponseBodyMethodProcessor 何時(shí)被調(diào)用

上面鋪墊了這么多,終于來(lái)了

RequestMappingHandlerAdapter 的 invokeHandlerMethod 中 構(gòu)建了 invocableMethod 對(duì)象并將所有的解析器和處理器封裝到該對(duì)象,通過(guò)invocableMethod.invokeAndHandle() 進(jìn)行對(duì)請(qǐng)求的解析,對(duì)controller的調(diào)用,以及響應(yīng)的處理

image.png

invocableMethod.invokeAndHandle() 中是怎么樣實(shí)現(xiàn)的

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
            
        // 參數(shù)解析,并反射調(diào)用controller方法,獲取方法返回值
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        
        // 下面就是對(duì)Response的處理
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                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;
        }
    }
    
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
            
        // 調(diào)用參數(shù)解析器獲取調(diào)用controller 所需的參數(shù)
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        
        // 反射調(diào)用 controller
        return doInvoke(args);
    }
    
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        if (ObjectUtils.isEmpty(getMethodParameters())) {
            return EMPTY_ARGS;
        }
        MethodParameter[] parameters = getMethodParameters();
        Object[] args = new Object[parameters.length];
        
        // 遍歷解析參數(shù)
        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;
            }
            
            // 這里的 resolvers 是一個(gè)封裝了所有參數(shù)解析器的包裝類(lèi),遍歷所有解析器,如果不能找到支持當(dāng)前參數(shù)的,拋出異常
            // 如果找到當(dāng)前參數(shù)對(duì)應(yīng)的解析器,則緩存起來(lái),在下面的 resolvers.resolveArgument 時(shí),直接使用
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 調(diào)用參數(shù)解析器
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                // Leave stack trace for later, exception may actually be resolved and handled..
                if (logger.isDebugEnabled()) {
                    String error = ex.getMessage();
                    if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, error));
                    }
                }
                throw ex;
            }
        }
        return args;
    }

invokeAndHandle() 里做了三件事

  1. 請(qǐng)求的封裝(將請(qǐng)求封裝為Controller中聲明的參數(shù))
  2. 使用封裝之后的參數(shù)反射調(diào)用 Controller 方法
  3. 處理響應(yīng)

梳理一下

請(qǐng)求封裝時(shí)會(huì)遍歷Controller中所聲明的所有參數(shù),為每一個(gè)參數(shù)找到對(duì)應(yīng)的參數(shù)解析器(如果找不到則拋異常),然后對(duì)其進(jìn)行解析。
參數(shù)解析器中又會(huì)根據(jù)當(dāng)前請(qǐng)求的content-type找到對(duì)應(yīng)HttpMessage轉(zhuǎn)器,最終將Http請(qǐng)求解析為我們聲明的參數(shù)。

當(dāng)拿到了封裝好的參數(shù)之后,通過(guò)反射調(diào)用Controller 方法

響應(yīng)處理邏輯同上

一次Http請(qǐng)求經(jīng)歷了什么

回過(guò)頭來(lái)再看,這時(shí)候我們發(fā)一個(gè)請(qǐng)求,在 RequestMappingHandlerAdapter 的 invokeHandlerMethod()中 debug一下,看一下線程棧是什么樣的

簡(jiǎn)單畫(huà)一張圖來(lái)表示一下

END

歡迎各位給出意見(jiàn)和指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容