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())); } }
- 从这段代码中我们可以知道
spring中默认添加了几个消息转换器,比如ByteArrayHttpMessageConverter,stringHttpMessageConverter,ResourceHttpMessageConverter等等
会根据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()); }