spring学习笔记(七)HttpMessageConverter

1. HttpMessageConverter的加载

  • 首先我们找到WebMvcAutoConfiguration这个类

    在其中我们可以看到这么一段代码

    @Configuration
    @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
    @Order(0)    //表示最先加载
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
            private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
            private final ResourceProperties resourceProperties;
            private final WebMvcProperties mvcProperties;
            private final ListableBeanFactory beanFactory;
            private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
            final WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
            private ResourceLoader resourceLoader;
    
            public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
                this.resourceProperties = resourceProperties;
                this.mvcProperties = mvcProperties;
                this.beanFactory = beanFactory;
                this.messageConvertersProvider = messageConvertersProvider;
                this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
            }
    
            public void setResourceLoader(ResourceLoader resourceLoader) {
                this.resourceLoader = resourceLoader;
            }
    
            public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {          this.messageConvertersProvider.ifAvailable((customConverters) -> {
                // 添加自定义的converter
                    converters.addAll(customConverters.getConverters());
                });
            }
         // 省略一部分不重要的代码
    	.....
    
  • 上面我们要分析的核心代码就是configureMessageConverters(List<HttpMessageConverter<?>> converters)这个方法,首先我要知道这个方法的入参是什么,在哪里初始化的。

  • 我们可以看到在这个类上有一个@import注解

    @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})

  • 我们可以跟一下这个类----》EnableWebMvcConfiguration

  • 我们可以发现这个类继承了WebMvcConfigurationSupport,核心的关于messageConverter的代码如下:

    protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
            StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
            stringHttpMessageConverter.setWriteAcceptCharset(false);
            messageConverters.add(new ByteArrayHttpMessageConverter());
            messageConverters.add(stringHttpMessageConverter);
            messageConverters.add(new ResourceHttpMessageConverter());
            messageConverters.add(new ResourceRegionHttpMessageConverter());
    
            try {
                messageConverters.add(new SourceHttpMessageConverter());
            } catch (Throwable var4) {
            }
    
            messageConverters.add(new AllEncompassingFormHttpMessageConverter());
            if (romePresent) {
                messageConverters.add(new AtomFeedHttpMessageConverter());
                messageConverters.add(new RssChannelHttpMessageConverter());
            }
    
            Jackson2ObjectMapperBuilder builder;
            if (jackson2XmlPresent) {
                builder = Jackson2ObjectMapperBuilder.xml();
                if (this.applicationContext != null) {
                    builder.applicationContext(this.applicationContext);
                }
    
                messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
            } else if (jaxb2Present) {
                messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
            }
    
            if (jackson2Present) {
                builder = Jackson2ObjectMapperBuilder.json();
                if (this.applicationContext != null) {
                    builder.applicationContext(this.applicationContext);
                }
    
                messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
            } else if (gsonPresent) {
                messageConverters.add(new GsonHttpMessageConverter());
            } else if (jsonbPresent) {
                messageConverters.add(new JsonbHttpMessageConverter());
            }
    
            if (jackson2SmilePresent) {
                builder = Jackson2ObjectMapperBuilder.smile();
                if (this.applicationContext != null) {
                    builder.applicationContext(this.applicationContext);
                }
    
                messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
            }
    
            if (jackson2CborPresent) {
                builder = Jackson2ObjectMapperBuilder.cbor();
                if (this.applicationContext != null) {
                    builder.applicationContext(this.applicationContext);
                }
    
                messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
            }
    
        }
    
  • 从这段代码中我们可以知道

    1. spring中默认添加了几个消息转换器,比如ByteArrayHttpMessageConverter,stringHttpMessageConverter,ResourceHttpMessageConverter等等
    2. 会根据Jackson,Gson等添加对应的小心转换器

2. 从StringMessageConverter探究消息转换器的原理

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
    // 默认字符集
    public static final Charset DEFAULT_CHARSET;
    @Nullable
    private volatile List<Charset> availableCharsets;
    private boolean writeAcceptCharset;

    public StringHttpMessageConverter() {
        this(DEFAULT_CHARSET);
    }

    public StringHttpMessageConverter(Charset defaultCharset) {
        super(defaultCharset, new MediaType[]{MediaType.TEXT_PLAIN, MediaType.ALL});
        this.writeAcceptCharset = true;
    }

    public void setWriteAcceptCharset(boolean writeAcceptCharset) {
        this.writeAcceptCharset = writeAcceptCharset;
    }

    public boolean supports(Class<?> clazz) {
        return String.class == clazz;
    }

    protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
        Charset charset = this.getContentTypeCharset(inputMessage.getHeaders().getContentType());
        return StreamUtils.copyToString(inputMessage.getBody(), charset);
    }

    protected Long getContentLength(String str, @Nullable MediaType contentType) {
        Charset charset = this.getContentTypeCharset(contentType);
        return (long)str.getBytes(charset).length;
    }

    protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
        if (this.writeAcceptCharset) {
            outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets());
        }

        Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
        StreamUtils.copy(str, charset, outputMessage.getBody());
    }

    protected List<Charset> getAcceptedCharsets() {
        List<Charset> charsets = this.availableCharsets;
        if (charsets == null) {
            charsets = new ArrayList(Charset.availableCharsets().values());
            this.availableCharsets = (List)charsets;
        }

        return (List)charsets;
    }

    private Charset getContentTypeCharset(@Nullable MediaType contentType) {
        if (contentType != null && contentType.getCharset() != null) {
            return contentType.getCharset();
        } else if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
            return StandardCharsets.UTF_8;
        } else {
            Charset charset = this.getDefaultCharset();
            Assert.state(charset != null, "No default charset");
            return charset;
        }
    }

    static {
        // 默认字符集为ISO-8859-1
        DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
    }
}

​ 上面代码的核心就在两个读写的方法上,分别是将字符串转换为httpMessage,跟将httpMessage转换为字符串

// 读方法
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
        Charset charset = 
   // 根据请求头中的信息,得到编码类型并转换         
   this.getContentTypeCharset(inputMessage.getHeaders().getContentType());
        return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
// 写方法
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
        if (this.writeAcceptCharset) {
            outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets());
        }

        Charset charset = 
         // 根据响应头中的编码类型,将字符串转换为httpMessage消息  
            this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
        StreamUtils.copy(str, charset, outputMessage.getBody());
    }

posted @ 2019-03-30 00:26  明智说  阅读(285)  评论(0编辑  收藏  举报