手记

ResponseBody

返回值处理器

返回值处理器就是专门处理Handler方法的返回值的,比如执行完Controller中的一个具体方法,返回一个普通对象,如果此方法用@ResponseBody标注了,则返回的是一个JSON字符串,此时就是RequestResponseBodyMethodProcessor来处理的,我们看下返回值处理器的定义

public interface HandlerMethodReturnValueHandler {
    /**
     * Whether the given {@linkplain MethodParameter method return type} is
     * supported by this handler.
     * @param returnType the method return type to check
     * @return {@code true} if this handler supports the supplied return type;
     * {@code false} otherwise
     * 根据指定的
     */
    boolean supportsReturnType(MethodParameter returnType);
    /**
     * Handle the given return value by adding attributes to the model and
     * setting a view or setting the
     * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
     * to indicate the response has been handled directly.
     * @param returnValue the value returned from the handler method
     * @param returnType the type of the return value. This type must have
     * previously been passed to {@link #supportsReturnType} which must
     * have returned {@code true}.
     * @param mavContainer the ModelAndViewContainer for the current request
     * @param webRequest the current request
     * @throws Exception if the return value handling results in an error
     */
    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

RequestResponseBodyMethodProcessor 源码

    //判断是否支持此类型
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }
    //处理返回值
    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);
    }

初始化

因为后续一系列操作都是HandlerAdapter来完成的,所以参数解析器和返回值处理器都是在HandlerAdapter中初始化完成的
返回值处理器的初始化HandlerAdapter中完成,看下RequestMappingHandlerAdapter的源码,因为实现了InitializingBean接口,所以spring容器在加载bean时会执行void afterPropertiesSet() 方法

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

getDefaultReturnValueHandlers()方法就是初始化返回值处理器的方法,会默认加载很多spring自定义的返回值处理器,基本满足绝大部分需求,其中的RequestResponseBodyMethodProcessor,也就是我们本章要学习的用来处理带有@ResponseBody注解的 返回值处理器

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));
        handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));
        // Multi-purpose return value types
        handlers.add(new ViewNameMethodReturnValueHandler());
        handlers.add(new MapMethodProcessor());
        // Custom return value types
        if (getCustomReturnValueHandlers() != null) {
            handlers.addAll(getCustomReturnValueHandlers());
        }
        // Catch-all
        if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
            handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
        }
        else {
            handlers.add(new ModelAttributeMethodProcessor(true));
        }
        return handlers;
    }

消息转换器

首先说明下HandlerAdapter的初始化是由标签mvc:annotation-driven来实现的,而标签的解析是由AnnotationDrivenBeanDefinitionParser来完成的,在加载过程中会初始化消息转换器messageConverters
如下代码可以解释为什么我们在使用JSON的时候,需要引用jackson相关的jar包,代码说明了如果类路径下存在某个类,则加载相应的消息转换器,针对注解ResponseBody,输出是JSON数据,需要MappingJackson2HttpMessageConverter这个消息转换器来处理的,所以当我们使用ResponseBody,也同时要引用jackson的jar包

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
    public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();
    public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();
    public static final String CONTENT_NEGOTIATION_MANAGER_BEAN_NAME = "mvcContentNegotiationManager";
    private static final boolean javaxValidationPresent =
            ClassUtils.isPresent("javax.validation.Validator",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static boolean romePresent =
            ClassUtils.isPresent("com.rometools.rome.feed.WireFeed",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static final boolean jaxb2Present =
            ClassUtils.isPresent("javax.xml.bind.Binder",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static final boolean jackson2Present =
            ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
            ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static final boolean jackson2XmlPresent =
            ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static final boolean jackson2SmilePresent =
            ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static final boolean jackson2CborPresent =
            ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory",
                    AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
    private static final boolean gsonPresent =
            ClassUtils.isPresent("com.google.gson.Gson", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
}

private ManagedList<?> getMessageConverters(Element element, @Nullable Object source, ParserContext parserContext) {
        Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
        ManagedList<? super Object> messageConverters = new ManagedList<>();
        if (convertersElement != null) {
            messageConverters.setSource(source);
            for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
                Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
                messageConverters.add(object);
            }
        }
        if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
            messageConverters.setSource(source);
            messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));
            RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
            stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
            messageConverters.add(stringConverterDef);
            messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
            messageConverters.add(createConverterDefinition(ResourceRegionHttpMessageConverter.class, source));
            messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
            messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));
            if (romePresent) {
                messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
                messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
            }
            if (jackson2XmlPresent) {
                Class<?> type = MappingJackson2XmlHttpMessageConverter.class;
                RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
                GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
                jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
                jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
                messageConverters.add(jacksonConverterDef);
            }
            else if (jaxb2Present) {
                messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
            }
            if (jackson2Present) {
                Class<?> type = MappingJackson2HttpMessageConverter.class;
                RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
                GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
                jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
                messageConverters.add(jacksonConverterDef);
            }
            else if (gsonPresent) {
                messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
            }
            if (jackson2SmilePresent) {
                Class<?> type = MappingJackson2SmileHttpMessageConverter.class;
                RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
                GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
                jacksonFactoryDef.getPropertyValues().add("factory", new SmileFactory());
                jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
                messageConverters.add(jacksonConverterDef);
            }
            if (jackson2CborPresent) {
                Class<?> type = MappingJackson2CborHttpMessageConverter.class;
                RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
                GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
                jacksonFactoryDef.getPropertyValues().add("factory", new CBORFactory());
                jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
                messageConverters.add(jacksonConverterDef);
            }
        }
        return messageConverters;
    }

找到对应的HttpMessageConverter,调用write方法,可以直接将Controller中的Action返回的结果写入到输出流中,调用flush方法,把数据输入到客户端,其实也就是直接操作HttpServletResponse这个输出流,然后向客户端输入数据

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        Object outputValue;
        Class<?> valueType;
        Type declaredType;
        if (value instanceof CharSequence) {
            outputValue = value.toString();
            valueType = String.class;
            declaredType = String.class;
        }
        else {
            outputValue = value;
            valueType = getReturnValueType(outputValue, returnType);
            declaredType = getGenericType(returnType);
        }
        if (isResourceType(value, returnType)) {
            outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
            if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null) {
                Resource resource = (Resource) value;
                try {
                    List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
                    outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
                    outputValue = HttpRange.toResourceRegions(httpRanges, resource);
                    valueType = outputValue.getClass();
                    declaredType = 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());
                }
            }
        }
        List<MediaType> mediaTypesToUse;
        MediaType contentType = outputMessage.getHeaders().getContentType();
        if (contentType != null && contentType.isConcrete()) {
            mediaTypesToUse = Collections.singletonList(contentType);
        }
        else {
            HttpServletRequest request = inputMessage.getServletRequest();
            List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
            List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
            if (outputValue != null && producibleMediaTypes.isEmpty()) {
                throw new HttpMessageNotWritableException(
                        "No converter found for return value of type: " + valueType);
            }
            mediaTypesToUse = new ArrayList<>();
            for (MediaType requestedType : requestedMediaTypes) {
                for (MediaType producibleType : producibleMediaTypes) {
                    if (requestedType.isCompatibleWith(producibleType)) {
                        mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
                    }
                }
            }
            if (mediaTypesToUse.isEmpty()) {
                if (outputValue != null) {
                    throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
                }
                return;
            }
            MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
        }
        MediaType selectedMediaType = null;
        for (MediaType mediaType : mediaTypesToUse) {
            if (mediaType.isConcrete()) {
                selectedMediaType = mediaType;
                break;
            }
            else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
                selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                break;
            }
        }
        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(declaredType, valueType, selectedMediaType) :
                        converter.canWrite(valueType, selectedMediaType)) {
                    outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
                            (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                            inputMessage, outputMessage);
                    if (outputValue != null) {
                        addContentDispositionHeader(inputMessage, outputMessage);
                        if (genericConverter != null) {
                            genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);
                        }
                        else {
                            ((HttpMessageConverter) converter).write(outputValue, selectedMediaType, outputMessage);
                        }
                        if (logger.isDebugEnabled()) {
                            logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
                                    "\" using [" + converter + "]");
                        }
                    }
                    return;
                }
            }
        }
        if (outputValue != null) {
            throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
        }
    }

微信公众号:宋坤明
更多精彩请关内容,请参考  完整系列



1人推荐
随时随地看视频
慕课网APP