springMVC @ResponseBody 原理

  1. 前面说了适配器执行 handler 怎么解析请求参数,现在看怎么响应参数,还是从具体执行 handler 的方法开始

    // org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
        // 这里在解析参数,并拿到了方法的返回值
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);
    	...
        try {
            // 这里处理方法返回值,先获取返回值的处理对象
            this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }
    
  2. 怎么拿返回值处理对象。逻辑和处理器映射器、处理器适配器、参数解析器、参数消息转换器一致,都是挨个遍历,看哪个合适

    // org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
    @Override
    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);
    }
    
    // org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#selectHandler
    @Nullable
    private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
        boolean isAsyncValue = isAsyncReturnValue(value, returnType);
        // 看过太多这种逻辑了,都是遍历,找到一个合适的
        for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
            if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
                continue;
            }
            // 这里可以进去看下,最终是选择了 RequestResponseBodyMethodProcessor,为什么是它?看看 supportsReturnType 方法就知道了
            if (handler.supportsReturnType(returnType)) {
                return handler;
            }
        }
        return null;
    }
    
    // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsReturnType
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        // 使用了 @ResponseBody 的注解就使用 RequestResponseBodyMethodProcessor 来处理
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }
    
  3. 现在知道了 @ResponseBody 的注解就使用 RequestResponseBodyMethodProcessor 来处理,具体怎么处理?

    // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue
    @Override
    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);
    
        // 转型并写入响应流(根据源码是先获取 HttpMessageConvert 然后再转型,最后写入流)
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
    
  4. 拿 HttpMessageConvert 同请求参数解析一样,也要看 content-type 等一系列校验才能拿到合适的 HttpMessageConvert

    源码太多,只保留关键信息,把些判断都删掉,参数解析那里留下的源码多些,可以去那里看

    // org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)
    protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
        // 巴拉巴拉,王八念经
    
        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)) {
                    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) {
                            // 调用 HttpMessageConvert 的些操作,把数据写入响应流
                            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;
                }
            }
        }
    
    }
    

    贴个图便于理解

  5. 有了 HttpMessageConvert,这时开始转型和写入流

    // org.springframework.http.converter.GenericHttpMessageConverter#write
    @Override
    public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
            HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
    
        final HttpHeaders headers = outputMessage.getHeaders();
        addDefaultHeaders(headers, t, contentType);
    
        if (outputMessage instanceof StreamingHttpOutputMessage) {
            StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
            streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
                @Override
                public OutputStream getBody() {
                    return outputStream;
                }
                @Override
                public HttpHeaders getHeaders() {
                    return headers;
                }
            }));
        }
        else {
            // 这个方法又调用了本类的 writeInternal,本类的 writeInternal 又是个模板方法,所以到子类去看实现,然后就到了 jackson 了
            // 这个方法的作用是转型,方法体在下面
            writeInternal(t, type, outputMessage);
            // 刷新响应流,over
            outputMessage.getBody().flush();
        }
    }
    
    // org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal
    @Override
    protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
    
        MediaType contentType = outputMessage.getHeaders().getContentType();
        JsonEncoding encoding = getJsonEncoding(contentType);
        JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
        try {
            ...
                
            // 这里在进行些操作,也就是 jackson 的转型操作,有兴趣可以看看自行去 jackson 怎么做的,但本次的 @ResponseBody 原理已经结束了
            objectWriter.writeValue(generator, value);
            writeSuffix(generator, object);
            
            generator.flush();
        }
        catch (InvalidDefinitionException ex) {
            throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
        }
        catch (JsonProcessingException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
        }
    }
    
posted @ 2024-07-04 13:57  CyrusHuang  阅读(2)  评论(0编辑  收藏  举报