在我们日常的web开发过程中肯定都用过@RequestBody、@ResponseBody注解,其作用就是将输入输出参数解析成Json,但是Http请求和响应都是基于报文的,意味着浏览器与服务器之间的交互时通过原始文本进行数据同i性能的,这里其实就是HttpMessageConverter在起作用。

首先来看下HttpMessageConvertersAutoConfiguration配置类的源码

HttpMessageConvertersAutoConfiguration源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Configuration(proxyBeanMethods = false) //标明是配置类
@ConditionalOnClass(HttpMessageConverter.class)//在此类存在时生效
@Conditional(NotReactiveWebApplicationCondition.class) //判断非Reactive环境
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class }) //配置类执行顺序
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class,
JsonbHttpMessageConvertersConfiguration.class }) //引入配置
public class HttpMessageConvertersAutoConfiguration {

static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(StringHttpMessageConverter.class)
@EnableConfigurationProperties(HttpProperties.class)
protected static class StringHttpMessageConverterConfiguration {

@Bean
@ConditionalOnMissingBean
public StringHttpMessageConverter stringHttpMessageConverter(HttpProperties httpProperties) {
StringHttpMessageConverter converter = new StringHttpMessageConverter(
httpProperties.getEncoding().getCharset());
converter.setWriteAcceptCharset(false);
return converter;
}

}

static class NotReactiveWebApplicationCondition extends NoneNestedConditions {

NotReactiveWebApplicationCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnWebApplication(type = Type.REACTIVE)
private static class ReactiveWebApplication {

}

}

}

从源码中我们可以看到此配置类给Ioc容器中添加了messageConverters和stringHttpMessageConverter这两个bean

HttpMessageConvertersAutoConfiguration

首先先看这一段代码

1
2
3
4
5
6
@Bean
@ConditionalOnMissingBean //容器不存在时添加
//使用@Bean时 参数知己从容器中获取 实现注入
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}

执行完毕后会查询到这些converter
converters

其中我们可以看到StringHttpMessageConverter,这是为了处理不同编码。

image.png

StringHttpMessageConverter源码

我们可以看到如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
HttpHeaders headers = outputMessage.getHeaders();
if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {
headers.setAcceptCharset(getAcceptedCharsets());
}
Charset charset = getContentTypeCharset(headers.getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}

//---------------------------------------------------------
//为了方便 同时把StreamUtils.copy的代码放在这里

public static void copy(String in, Charset charset, OutputStream out) throws IOException {
Assert.notNull(in, "No input String specified");
Assert.notNull(charset, "No charset specified");
Assert.notNull(out, "No OutputStream specified");

//获取输出流
Writer writer = new OutputStreamWriter(out, charset);
//将数据写入
writer.write(in);
writer.flush();
}

当我们请求一个地址的时候,方法上加了@ResponseBody或者使用的是@RestControlelr,并且返回值是一个字符串,此converter就会生效
请求地址

断点数据
之后就用到StreamUtils的copy方法了,将我们返回的数据写入输出流,返回给浏览器。