Springboot响应处理源码分析(含源代码详细注释)


大纲

响应JSON

主要类:
ServletInvocableHandlerMethod.java
RequestResponseBodyMethodProcessor.java
主要方法:

			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                              ModelAndViewContainer mavContainer, NativeWebRequest webRequest)

其他类:
HandlerMethod.java
HandlerMethodReturnValueHandlerComposite.java
ModelAndViewMethodReturnValueHandler.java
RequestResponseBodyMethodProcessor.java
AbstractMessageConverterMethodProcessor.java
HttpMessageConverter.java
AbstractGenericHttpMessageConverter.java
AbstractJackson2HttpMessageConverter.java
在这里插入图片描述最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
在这里插入图片描述

内容协商

根据客户端接收能力不同,返回不同媒体类型的数据。
其他类:
ContentNegotiationManager.java
HeaderContentNegotiationStrategy.java

自定义 MessageConverter

SpringMVC的什么功能。一个入口给容器中添加一个 WebMvcConfigurer

@Bean

    public WebMvcConfigurer webMvcConfigurer(){

        return new WebMvcConfigurer() {


            @Override

            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {


            }

        }

    }

代码

ServletInvocableHandlerMethod.java

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

        // 执行当前请求,断点去到Controller里面的目标方法
        // 真正执行了目标方法
        // 确定目标方法每一个参数值
        // mavContainer中的defaultModel存储了参数的值
        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 {
            // 处理返回结果,传进去mavContainer中携带了参数的值
            // getReturnValueType(returnValue)得到返回值的类型
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }
}

HandlerMethod.java

public class HandlerMethod {
    public MethodParameter getReturnValueType(@Nullable Object returnValue) {
        return new ReturnValueMethodParameter(returnValue);
    }
    private class ReturnValueMethodParameter extends HandlerMethodParameter {

        @Nullable
        private final Object returnValue;

        public ReturnValueMethodParameter(@Nullable Object returnValue) {
            super(-1);
            this.returnValue = returnValue;
        }
    }
}

HandlerMethodReturnValueHandlerComposite.java

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
    @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);
    }

    @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;
            }
            // 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
            // 2、返回值处理器调用 handleReturnValue 进行处理
            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;
    }
}

ModelAndViewMethodReturnValueHandler.java

public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        // 返回值是不是ModelAndView
        return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
    }

}

RequestResponseBodyMethodProcessor.java

/**
 * 处理返回值标注了ResponseBody的
 */
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    @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);

        // Try even with null return value. ResponseBodyAdvice could get involved.
        // 使用消息转换器进行写出操作
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
}

AbstractMessageConverterMethodProcessor.java

public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
        implements HandlerMethodReturnValueHandler {

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

        Object body;
        Class<?> valueType;
        Type targetType;

        if (value instanceof CharSequence) {
            body = value.toString();
            valueType = String.class;
            targetType = String.class;
        }
        else {
            body = value;
            // 值类型
            valueType = getReturnValueType(body, returnType);
            // 要转的目标类型
            targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
        }

        // 是不是资源类型,流数据
        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());
                }
            }
        }

        // MediaType媒体类型牵扯到内容协商
        // 内容协商就是浏览器会以请求头的方式告诉服务器能接受的内容Accept,有权重
        MediaType selectedMediaType = null;
        // 判断当前响应头中是否已经有确定的媒体类型。MediaType
        MediaType contentType = outputMessage.getHeaders().getContentType();
        boolean isContentTypePreset = contentType != null && contentType.isConcrete();
        if (isContentTypePreset) {
            if (logger.isDebugEnabled()) {
                logger.debug("Found 'Content-Type:" + contentType + "' in response");
            }
            // 拿到前面处理的响应头的内容类型
            selectedMediaType = contentType;
        }
        else {
            HttpServletRequest request = inputMessage.getServletRequest();
            // acceptableTypes = {ArrayList@8821}  size = 5
            // 0 = {MediaType@8825} "text/html"
            // 1 = {MediaType@8826} "application/xhtml+xml"
            // 2 = {MediaType@8827} "image/webp"
            // 3 = {MediaType@8828} "application/xml;q=0.9"
            // 4 = {MediaType@8829} "*/*;q=0.8"
            // 浏览器能接受的内容类型
            // 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)
            List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
            // 0 = {MediaType@8840} "application/json"
            // 1 = {MediaType@8841} "application/*+json"
            // 2 = {MediaType@8840} "application/json"
            // 3 = {MediaType@8842} "application/*+json"
            // 4 = {MediaType@8843} "application/xml;charset=UTF-8"
            // 5 = {MediaType@8844} "text/xml;charset=UTF-8"
            // 6 = {MediaType@8845} "application/*+xml;charset=UTF-8"
            // 7 = {MediaType@8846} "application/xml;charset=UTF-8"
            // 8 = {MediaType@8847} "text/xml;charset=UTF-8"
            // 9 = {MediaType@8848} "application/*+xml;charset=UTF-8"
            // 服务器能响应的类型
            List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

            if (body != null && producibleTypes.isEmpty()) {
                throw new HttpMessageNotWritableException(
                        "No converter found for return value of type: " + valueType);
            }
            // mediaTypesToUse存放了我们能写出什么类型的内容数据
            // 进行内容协商的最佳匹配媒体类型
            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();
            // 0 = {ByteArrayHttpMessageConverter@8885}
            // 1 = {StringHttpMessageConverter@8886}
            // 2 = {StringHttpMessageConverter@8887}
            // 3 = {ResourceHttpMessageConverter@8888}
            // 4 = {ResourceRegionHttpMessageConverter@8889}
            // 5 = {SourceHttpMessageConverter@8890}
            // 6 = {AllEncompassingFormHttpMessageConverter@8891}
            // 7 = {MappingJackson2HttpMessageConverter@8892}
            // 8 = {MappingJackson2HttpMessageConverter@8893}
            // 9 = {MappingJackson2XmlHttpMessageConverter@8894}
            // 10 = {MappingJackson2XmlHttpMessageConverter@8895}
            // SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能将对象转为xml
            // 用支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
            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) {
                            // 最终 MappingJackson2HttpMessageConverter  把对象转为JSON(利用底层的jackson的objectMapper转换的)
                            genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                        }
                        else {
                            ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                        }
                    }
                    else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Nothing to write: null body");
                        }
                    }
                    return;
                }
            }
        }

        if (body != null) {
            Set<MediaType> producibleMediaTypes =
                    (Set<MediaType>) inputMessage.getServletRequest()
                            .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

            if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
                throw new HttpMessageNotWritableException(
                        "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
            }
            throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));
        }
    }

    protected boolean isResourceType(@Nullable Object value, MethodParameter returnType) {
        Class<?> clazz = getReturnValueType(value, returnType);
        return clazz != InputStreamResource.class && Resource.class.isAssignableFrom(clazz);
    }

    protected List<MediaType> getProducibleMediaTypes(
            HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {

        Set<MediaType> mediaTypes =
                (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        if (!CollectionUtils.isEmpty(mediaTypes)) {
            return new ArrayList<>(mediaTypes);
        }
        List<MediaType> result = new ArrayList<>();
        // 遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
        //客户端需要【application/xml】。服务端能力【10种、json、xml】
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            if (converter instanceof GenericHttpMessageConverter && targetType != null) {
                if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
                    // 找到支持操作Person的converter,把converter支持的媒体类型统计出来。
                    result.addAll(converter.getSupportedMediaTypes(valueClass));
                }
            }
            else if (converter.canWrite(valueClass, null)) {
                result.addAll(converter.getSupportedMediaTypes(valueClass));
            }
        }
        return (result.isEmpty() ? Collections.singletonList(MediaType.ALL) : result);
    }

    private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
            throws HttpMediaTypeNotAcceptableException {

        return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
    }
}

HttpMessageConverter.java

/**
 * 消息转换器
 * HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
 * 例子:Person对象转为JSON。或者 JSON转为Person
 * @param <T>
 */
public interface HttpMessageConverter<T> {

    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

    List<MediaType> getSupportedMediaTypes();

    default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
        return (canRead(clazz, null) || canWrite(clazz, null) ?
                getSupportedMediaTypes() : Collections.emptyList());
    }

    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException;

    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;

}

AbstractGenericHttpMessageConverter.java

public abstract class AbstractGenericHttpMessageConverter<T> extends AbstractHttpMessageConverter<T>
        implements GenericHttpMessageConverter<T> {
    @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 {
            // type:Person类型写给响应outputMessage
            writeInternal(t, type, outputMessage);
            outputMessage.getBody().flush();
        }
    }

}

AbstractJackson2HttpMessageConverter.java

public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
    @Override
    protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        MediaType contentType = outputMessage.getHeaders().getContentType();
        JsonEncoding encoding = getJsonEncoding(contentType);

        Class<?> clazz = (object instanceof MappingJacksonValue ?
                ((MappingJacksonValue) object).getValue().getClass() : object.getClass());
        ObjectMapper objectMapper = selectObjectMapper(clazz, contentType);
        Assert.state(objectMapper != null, "No ObjectMapper for " + clazz.getName());

        OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
        try (JsonGenerator generator = objectMapper.getFactory().createGenerator(outputStream, encoding)) {
            writePrefix(generator, object);

            Object value = object;
            Class<?> serializationView = null;
            FilterProvider filters = null;
            JavaType javaType = null;

            if (object instanceof MappingJacksonValue) {
                MappingJacksonValue container = (MappingJacksonValue) object;
                value = container.getValue();
                serializationView = container.getSerializationView();
                filters = container.getFilters();
            }
            if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
                javaType = getJavaType(type, null);
            }

            ObjectWriter objectWriter = (serializationView != null ?
                    objectMapper.writerWithView(serializationView) : objectMapper.writer());
            if (filters != null) {
                objectWriter = objectWriter.with(filters);
            }
            if (javaType != null && javaType.isContainerType()) {
                objectWriter = objectWriter.forType(javaType);
            }
            SerializationConfig config = objectWriter.getConfig();
            if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
                    config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
                objectWriter = objectWriter.with(this.ssePrettyPrinter);
            }
            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);
        }
    }
}

ContentNegotiationManager.java

public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
        // 使用请求头的内容策略来解析媒体类型
        for (ContentNegotiationStrategy strategy : this.strategies) {
            List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
            if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
                continue;
            }
            return mediaTypes;
        }
        return MEDIA_TYPE_ALL_LIST;
    }
}

HeaderContentNegotiationStrategy.java

public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest request)
            throws HttpMediaTypeNotAcceptableException {

        // 获取请求头的ACCEPT字段
        String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
        if (headerValueArray == null) {
            return MEDIA_TYPE_ALL_LIST;
        }

        List<String> headerValues = Arrays.asList(headerValueArray);
        try {
            List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
            MediaType.sortBySpecificityAndQuality(mediaTypes);
            return !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;
        }
        catch (InvalidMediaTypeException ex) {
            throw new HttpMediaTypeNotAcceptableException(
                    "Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage());
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值