spring boot @ResponseBody注解

8 篇文章 0 订阅
8 篇文章 0 订阅

@ResponseBody注解,标记为此注解的controller,method,return,返回数据将被转换成Json格式数据给客户端

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}

具体调用代码流程如下:
DispatcherServlet.doDispatch->
RequestMappingHandlerAdapter.handle
->RequestMappingHandlerAdapter.handleInternal
->RequestMappingHandlerAdapter.invokeHandlerMethod
->ServletInvocableHandlerMethod.invokeAndHandle
->HandlerMethodReturnValueHandlerComposite.handleReturnValue
->HandlerMethodReturnValueHandlerComposite.selectHandler
->RequestResponseBodyMethodProcessor.handleReturnValue
->RequestResponseBodyMethodProcessor.writeWithMessageConverters
->MappingJackson2HttpMessageConverter.write

HandlerMethodReturnValueHandlerComposite的方法selectHandler将调用所有HandlerMethodReturnValueHandler的supportsReturnType方法查找符合要求的处理器。


@Nullable
    private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
        boolean isAsyncValue = this.isAsyncReturnValue(value, returnType);
        Iterator var4 = this.returnValueHandlers.iterator();

        HandlerMethodReturnValueHandler handler;
        do {
            do {
                if (!var4.hasNext()) {
                    return null;
                }

                handler = (HandlerMethodReturnValueHandler)var4.next();
            } while(isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler));
        } while(!handler.supportsReturnType(returnType));

        return handler;
    }
    

如果controller中包含@ResponseBody,那么RequestResponseBodyMethodProcessor将被引用,如下代码:

RequestResponseBodyMethodProcessor:
public boolean supportsReturnType(MethodParameter returnType) {
    return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class);
}

RequestResponseBodyMethodProcessor的writeWithMessageConverters将过滤所有的处转换器,根据request的MediaType去找到可用的converter, 默认标记为@ResonseBody使用MappingJackson2HttpMessageConverter将返回数据转换成json.

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        Object body;
        Class valueType;
        Object targetType;
        if (value instanceof CharSequence) {
            body = value.toString();
            valueType = String.class;
            targetType = String.class;
        } else {
            body = value;
            valueType = this.getReturnValueType(value, returnType);
            targetType = GenericTypeResolver.resolveType(this.getGenericType(returnType), returnType.getContainingClass());
        }

        if (this.isResourceType(value, returnType)) {
            outputMessage.getHeaders().set("Accept-Ranges", "bytes");
            if (value != null && inputMessage.getHeaders().getFirst("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 var19) {
                    outputMessage.getHeaders().set("Content-Range", "bytes */" + resource.contentLength());
                    outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
                }
            }
        }

        MediaType selectedMediaType = null;
        MediaType contentType = outputMessage.getHeaders().getContentType();
        boolean isContentTypePreset = contentType != null && contentType.isConcrete();
        if (isContentTypePreset) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Found 'Content-Type:" + contentType + "' in response");
            }

            selectedMediaType = contentType;
        } else {
            HttpServletRequest request = inputMessage.getServletRequest();
            List<MediaType> acceptableTypes = this.getAcceptableMediaTypes(request);
            List<MediaType> producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);
            if (body != null && producibleTypes.isEmpty()) {
                throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
            }

            List<MediaType> mediaTypesToUse = new ArrayList();
            Iterator var15 = acceptableTypes.iterator();

            MediaType mediaType;
            while(var15.hasNext()) {
                mediaType = (MediaType)var15.next();
                Iterator var17 = producibleTypes.iterator();

                while(var17.hasNext()) {
                    MediaType producibleType = (MediaType)var17.next();
                    if (mediaType.isCompatibleWith(producibleType)) {
                        mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));
                    }
                }
            }

            if (mediaTypesToUse.isEmpty()) {
                if (body != null) {
                    throw new HttpMediaTypeNotAcceptableException(producibleTypes);
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
                }

                return;
            }

            MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
            var15 = mediaTypesToUse.iterator();

            while(var15.hasNext()) {
                mediaType = (MediaType)var15.next();
                if (mediaType.isConcrete()) {
                    selectedMediaType = mediaType;
                    break;
                }

                if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
                    selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                    break;
                }
            }

            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes);
            }
        }

        HttpMessageConverter converter;
        GenericHttpMessageConverter genericConverter;
        label159: {
            if (selectedMediaType != null) {
                selectedMediaType = selectedMediaType.removeQualityValue();
                Iterator var22 = this.messageConverters.iterator();

                while(var22.hasNext()) {
                    converter = (HttpMessageConverter)var22.next();
                    genericConverter = converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter)converter : null;
                    if (genericConverter != null) {
                        if (((GenericHttpMessageConverter)converter).canWrite((Type)targetType, valueType, selectedMediaType)) {
                            break label159;
                        }
                    } else if (converter.canWrite(valueType, selectedMediaType)) {
                        break label159;
                    }
                }
            }

            if (body != null) {
                Set<MediaType> producibleMediaTypes = (Set)inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
                if (!isContentTypePreset && CollectionUtils.isEmpty(producibleMediaTypes)) {
                    throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
                }

                throw new HttpMessageNotWritableException("No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
            }

            return;
        }

        body = this.getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, converter.getClass(), inputMessage, outputMessage);
        if (body != null) {
            LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
                return "Writing [" + LogFormatUtils.formatValue(body, !traceOn) + "]";
            });
            this.addContentDispositionHeader(inputMessage, outputMessage);
            if (genericConverter != null) {
                genericConverter.write(body, (Type)targetType, selectedMediaType, outputMessage);
            } else {
                converter.write(body, selectedMediaType, outputMessage);
            }
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("Nothing to write: null body");
        }

    }

MappingJackson2HttpMessageConverter使com.fasterxml.jackson.databind.ObjectMapper转换Object到Json, 所有我们可以配置Jackson的一些属性值,

如:spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

应用启动时spring autoconfigure jar中JacksonAutoConfiguration会将这些配置载入到JacksonProperties中,Jackson2ObjectMapperBuilder将组装这些配置注入到ObjectMapper中。

@ConfigurationProperties(
    prefix = "spring.jackson"
)
public class JacksonProperties {
    private String dateFormat;
    private String propertyNamingStrategy;
    private final Map<PropertyAccessor, Visibility> visibility = new EnumMap(PropertyAccessor.class);
    private final Map<SerializationFeature, Boolean> serialization = new EnumMap(SerializationFeature.class);
    private final Map<DeserializationFeature, Boolean> deserialization = new EnumMap(DeserializationFeature.class);
    private final Map<MapperFeature, Boolean> mapper = new EnumMap(MapperFeature.class);
    private final Map<Feature, Boolean> parser = new EnumMap(Feature.class);
    private final Map<com.fasterxml.jackson.core.JsonGenerator.Feature, Boolean> generator = new EnumMap(com.fasterxml.jackson.core.JsonGenerator.Feature.class);
    private Include defaultPropertyInclusion;
    private TimeZone timeZone = null;
    private Locale locale;
    .....
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值