美文网首页springMVC源码分析
springMVC源码分析--HttpMessageConver

springMVC源码分析--HttpMessageConver

作者: carway | 来源:发表于2017-10-16 21:25 被阅读90次

    上一篇文章 springMVC源码分析--HttpMessageConverter(二)之参数read操作中我们已经简单介绍了参数值转换的read操作,接下来我们介绍一下返回值的处理操作。同样返回值的操作操作也是在HandlerMethodReturnValueHandler中处理的,可以参考之前的springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)

    springmvc读写的过程.png

    简单的返回值处理示例使用@ResponseBody进行注解:

        @ResponseBody  
        @RequestMapping("/get")  
        public Object get(){  
            Product product = new Product();  
            product.setDescription("hello  springMVC  RestFul");  
            product.setId(10);  
            product.setName("springMVC");  
            product.setPrice(10);  
            return product; //在页面中返回json数据  
        }  
    

    这里返回值是一个Product对象,但真正浏览器获取的数据是json数据,处理的过程就是在HttpMessageConverter中实现的 。
    配置一下消息处理使用FastJSON处理的配置

    普通的spring项目的话,用xml配置

    <mvc:annotation-driven>  
            <mvc:message-converters register-defaults="true">  
                <bean  
                    class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">  
                    <property name="supportedMediaTypes">  
                        <array>  
                            <value>text/html;charset=UTF-8</value>  
                        </array>  
                    </property>  
                    <property name="features">  
                        <array>  
                            <value>WriteMapNullValue</value>  
                            <value>WriteNullStringAsEmpty</value>  
                            <!-- 全局关闭循环引用检查,最好是不要关闭,不然有可能会StackOverflowException -->
                           <!-- <value>DisableCircularReferenceDetect</value>  -->
                        </array>  
                    </property>  
                </bean>  
            </mvc:message-converters>  
        </mvc:annotation-driven>  
    

    如果springboot的话

    public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter{
        public FastJsonHttpMessageConverterEx(){
            //在这里配置fastjson特性(全局设置的)
            FastJsonConfig fastJsonConfig = new FastJsonConfig();
            //fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");    //自定义时间格式
            //fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);  //正常转换null值
            //fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);     //关闭循环引用
            this.setFastJsonConfig(fastJsonConfig);
        }
    
        @Override
        protected boolean supports(Class<?> clazz) {
            return super.supports(clazz);
        }
    }
    
    @Configuration
    public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
        .....
        @Bean
        public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx(){
            return new FastJsonHttpMessageConverterEx();
        }
    }
    

    这样返回值的处理操作就是使用FastJsonHttpMessageConverter来进行处理,将返回值转为json数据返回。
    返回值的处理是在HandlerMethodReturnValueHandlerComposite中handleReturnValue中实现的。

        @Override  
        public void handleReturnValue(Object returnValue, MethodParameter returnType,  
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {  
      
            HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);  
            Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");  
            handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);  
        }  
    

    接下来是在子类RequestResponseBodyMethodProcessor的handleReturnValue中处理操作

    public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
        public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) 
                                               throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
            mavContainer.setRequestHandled(true);
            ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
            ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
            this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
        }
    
    }
       
    

    在RequestResponseBodyMethodProcessor的父类AbstractMessageConverterMethodProcessor的writeWithMessageConverters中处理操作

        protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
            ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
            ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
            this.writeWithMessageConverters(value, returnType, inputMessage, outputMessage);
        }
    

    接下来就是通过职责链模式选择HttpMessageConverter的实现类来进行数据转换操作。
    会调用converter的write()方法,write()方法又会调用writeInternal()方法

    protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
            Object outputValue;
            Class valueType;
            Object declaredType;
            if(value instanceof CharSequence) {
                outputValue = value.toString();
                valueType = String.class;
                declaredType = String.class;
            } else {
                outputValue = value;
                valueType = this.getReturnValueType(value, returnType);
                declaredType = this.getGenericType(returnType);
            }
    
            HttpServletRequest request = inputMessage.getServletRequest();
            List requestedMediaTypes = this.getAcceptableMediaTypes(request);
            List producibleMediaTypes = this.getProducibleMediaTypes(request, valueType, (Type)declaredType);
            if(outputValue != null && producibleMediaTypes.isEmpty()) {
                throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
            } else {
                LinkedHashSet compatibleMediaTypes = new LinkedHashSet();
                Iterator mediaTypes = requestedMediaTypes.iterator();
    
                MediaType selectedMediaType;
                Iterator var14;
                MediaType messageConverter;
                while(mediaTypes.hasNext()) {
                    selectedMediaType = (MediaType)mediaTypes.next();
                    var14 = producibleMediaTypes.iterator();
    
                    while(var14.hasNext()) {
                        messageConverter = (MediaType)var14.next();
                        if(selectedMediaType.isCompatibleWith(messageConverter)) {
                            compatibleMediaTypes.add(this.getMostSpecificMediaType(selectedMediaType, messageConverter));
                        }
                    }
                }
    
                if(compatibleMediaTypes.isEmpty()) {
                    if(outputValue != null) {
                        throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
                    }
                } else {
                    ArrayList mediaTypes1 = new ArrayList(compatibleMediaTypes);
                    MediaType.sortBySpecificityAndQuality(mediaTypes1);
                    selectedMediaType = null;
                    var14 = mediaTypes1.iterator();
    
                    while(var14.hasNext()) {
                        messageConverter = (MediaType)var14.next();
                        if(messageConverter.isConcrete()) {
                            selectedMediaType = messageConverter;
                            break;
                        }
    
                        if(messageConverter.equals(MediaType.ALL) || messageConverter.equals(MEDIA_TYPE_APPLICATION)) {
                            selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                            break;
                        }
                    }
                    
                    if(selectedMediaType != null) {
                        selectedMediaType = selectedMediaType.removeQualityValue();
                        //匹配消息数据转换器
                        var14 = this.messageConverters.iterator();
    
                        while(var14.hasNext()) {
                            HttpMessageConverter messageConverter1 = (HttpMessageConverter)var14.next();
                            if(messageConverter1 instanceof GenericHttpMessageConverter) {
                                if(((GenericHttpMessageConverter)messageConverter1).canWrite((Type)declaredType, valueType, selectedMediaType)) {
                                    outputValue = this.getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, messageConverter1.getClass(), inputMessage, outputMessage);
                                    if(outputValue != null) {
                                        this.addContentDispositionHeader(inputMessage, outputMessage);
                                        //进行消息转换成配置的格式 ,调用write方法
                                        ((GenericHttpMessageConverter)messageConverter1).write(outputValue, (Type)declaredType, selectedMediaType, outputMessage);
                                        if(this.logger.isDebugEnabled()) {
                                            this.logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter1 + "]");
                                        }
                                    }
    
                                    return;
                                }
                            } else if(messageConverter1.canWrite(valueType, selectedMediaType)) {
                                outputValue = this.getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, messageConverter1.getClass(), inputMessage, outputMessage);
                                if(outputValue != null) {
                                    this.addContentDispositionHeader(inputMessage, outputMessage);
                                    //进行消息转换成配置的格式 ,调用write方法
                                    messageConverter1.write(outputValue, selectedMediaType, outputMessage);
                                    if(this.logger.isDebugEnabled()) {
                                        this.logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter1 + "]");
                                    }
                                }
    
                                return;
                            }
                        }
                    }
    
                    if(outputValue != null) {
                        throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
                    }
                }
            }
        }
    

    write()方法又会调用writeInternal()方法

     public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
            final HttpHeaders headers = outputMessage.getHeaders();
            this.addDefaultHeaders(headers, t, contentType);
            if(outputMessage instanceof StreamingHttpOutputMessage) {
                StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)outputMessage;
                streamingOutputMessage.setBody(new Body() {
                    public void writeTo(final OutputStream outputStream) throws IOException {
                        //调用writeInternal()
                        AbstractHttpMessageConverter.this.writeInternal(t, new HttpOutputMessage() {
                            public OutputStream getBody() throws IOException {
                                return outputStream;
                            }
    
                            public HttpHeaders getHeaders() {
                                return headers;
                            }
                        });
                    }
                });
            } else {
                this.writeInternal(t, outputMessage);
                outputMessage.getBody().flush();
            }
    
        }
    

    最终会选择FastJsonHttpMessageConverter 的write方法中进行处理操作,就是将数据转换为json写到输出流中

    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
            ByteArrayOutputStream outnew = new ByteArrayOutputStream();
    
            try {
                HttpHeaders ex = outputMessage.getHeaders();
                SerializeFilter[] globalFilters = this.fastJsonConfig.getSerializeFilters();
                ArrayList allFilters = new ArrayList(Arrays.asList(globalFilters));
                boolean isJsonp = false;
                Object value = this.strangeCodeForJackson(object);
                if(value instanceof FastJsonContainer) {
                    FastJsonContainer len = (FastJsonContainer)value;
                    PropertyPreFilters filters = len.getFilters();
                    allFilters.addAll(filters.getFilters());
                    value = len.getValue();
                }
    
                if(value instanceof MappingFastJsonValue) {
                    isJsonp = true;
                    value = ((MappingFastJsonValue)value).getValue();
                } else if(value instanceof JSONPObject) {
                    isJsonp = true;
                }
    
                int len1 = this.writePrefix(outnew, object);
                //只要加了@ResponseBody这里fastjson会将返回值转换为json字符串
                len1 += JSON.writeJSONString(outnew, this.fastJsonConfig.getCharset(), value, this.fastJsonConfig.getSerializeConfig(), (SerializeFilter[])allFilters.toArray(new SerializeFilter[allFilters.size()]), this.fastJsonConfig.getDateFormat(), JSON.DEFAULT_GENERATE_FEATURE, this.fastJsonConfig.getSerializerFeatures());
                len1 += this.writeSuffix(outnew, object);
                if(isJsonp) {
                    ex.setContentType(APPLICATION_JAVASCRIPT);
                }
    
                if(this.fastJsonConfig.isWriteContentLength()) {
                    ex.setContentLength((long)len1);
                }
    
                outnew.writeTo(outputMessage.getBody());
            } catch (JSONException var14) {
                throw new HttpMessageNotWritableException("Could not write JSON: " + var14.getMessage(), var14);
            } finally {
                outnew.close();
            }
    
        }
    

    插入一些小知识
    @ResponseBody
    作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。

    使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;

    所以从上面的writeInternal方法,我们可以知道,当我们使用@ResponseBody这个注解时,会自动调用JSON.writeJSONString这个方法,这个方法和toJSONString() 方法类似,里面会带上我们在Spring里面配置的属性。
    附一种不好的编程习惯:
    在加了@ResponseBody注解的Controller中使用

    String result = JSON.toJSONString(obj);
    return result;
    

    这种情况就相当于JSON.toJSONString() 这句话执行了两次。

    springMVC默认提供了很多参数和结果值处理器,包括如下:
    (1)MappingJackson2HttpMessageConverter
    (2)GsonHttpMessageConverter
    (3)ByteArrayHttpMessageConverter
    (4)ObjectToStringHttpMessageConverter
    (5)ProtobufHttpMessageConverter
    (6)ResourceHttpMessageConverter
    (7)StringHttpMessageConverter
    (8)AllEncompassingFormHttpMessageConverter

    参考文章:

    http://blog.csdn.net/qq924862077/article/details/55271959
    http://blog.csdn.net/weixiaodedao/article/details/51790790

    相关文章

      网友评论

        本文标题:springMVC源码分析--HttpMessageConver

        本文链接:https://www.haomeiwen.com/subject/izpluxtx.html