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()));
} }
从这段代码中我们可以知道
- 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());
}