1.消息转换器的初始化
有些参数解析器或返回值解析器中需要使用HttpMessageConverter进行数据类型转换,例如RequestResponseBodyMethodProcessor、RequestPartMethodArgumentResolver、HttpEntityMethodProcessor等,这些HttpMessageConverter是在HandlerAdapter初始化时创建的,见RequestMappingHandlerAdapter
public RequestMappingHandlerAdapter() {
this.messageConverters = new ArrayList<>(4);
// ByteArrayHttpMessageConverter支持application/octet-stream、*/*
this.messageConverters.add(new ByteArrayHttpMessageConverter());
// StringHttpMessageConverter支持text/plain、*/*
this.messageConverters.add(new StringHttpMessageConverter());
try {
// SourceHttpMessageConverter支持text/xml、application/xml、application/*-xml.
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
}
// AllEncompassingFormHttpMessageConverter根据加载类的情况添加消息转换器
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
AllEncompassingFormHttpMessageConverter中会根据加载类的情况添加消息转换器,例如已加载类中有com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator时,会添加MappingJackson2HttpMessageConverter,此转换器能实现JSON格式和对象的互转、有com.fasterxml.jackson.dataformat.xml.XmlMapper时会添加MappingJackson2XmlHttpMessageConverter,此转换器能实现XML格式和对象的互转
2.使用消息转换器
HttpMessageConverter接口中定义了如下5个方法:
public interface HttpMessageConverter<T> {
// 判断目标类型及MediaType是否能被此转换器读取
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
// 判断目标类型及MediaType是否能被此转换器写出
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
// 获取支持的MediaType
List<MediaType> getSupportedMediaTypes();
// 从HttpInputMessage中读取数据并转换为对象
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
// 根据MediaType将对象转换为对应格式的数据并写入到HttpOutputMessage中
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
以application/json为例,通过HttpMessageConverter进行读写转换数据示例如下:
1)将响应数据从对象转换为JSON格式
public class Test01 {
public static void main(String[] args) throws IOException {
MockHttpOutputMessage message = new MockHttpOutputMessage();
// 对象转JSON格式的转换器
HttpMessageConverter<Object> converter = new MappingJackson2HttpMessageConverter();
if (converter.canWrite(User.class, MediaType.APPLICATION_JSON)) {
converter.write(new User("limin", 28), MediaType.APPLICATION_JSON, message);
// {"name":"limin","age":28}
System.out.println(message.getBodyAsString());
}
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private String name;
private int age;
}
2)将请求数据从对象转换为JSON格式
private static void test03() throws IOException {
MockHttpInputMessage message = new MockHttpInputMessage("{\"name\":\"limin\",\"age\":28}".getBytes(StandardCharsets.UTF_8));
// 对象转JSON格式的转换器
HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
if (converter.canRead(User.class, MediaType.APPLICATION_JSON)) {
Object result = converter.read(User.class, message);
// User{name='limin', age=28}
System.out.println(result);
}
}
3.MappingJackson2HttpMessageConverter
MappingJackson2HttpMessageConverter支持application/json及application/*+json,是最为常用的消息转换器
MappingJackson2HttpMessageConverter继承自AbstractJackson2HttpMessageConverter,HttpMessageConverter接口中的方法都在AbstractJackson2HttpMessageConverter中实现
1)判断能否读取
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return canRead(clazz, null, mediaType);
}
@Override
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
// 1.判断是否支持传入的mediaType
if (!canRead(mediaType)) {
return false;
}
JavaType javaType = getJavaType(type, contextClass);
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// 2.判断类能否反序列化
if (this.objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
// 如果不能反序列化,打印异常原因
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
// AbstractHttpMessageConverter#canRead
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
// 遍历支持的MediaType,判断传入的MediaType能否匹配
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
由于MediaType中可能存在通配符,因此需要判断能否匹配,例如text/*能匹配text/plain、text/html等,application/*+xml匹配application/soap+xml等
2)从HttpInputMessage中读取数据
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
// 从请求头content-type中获取MediaType
MediaType contentType = inputMessage.getHeaders().getContentType();
// 默认UTF-8
Charset charset = getCharset(contentType);
boolean isUnicode = ENCODINGS.containsKey(charset.name());
try {
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
ObjectReader objectReader = this.objectMapper.readerWithView(deserializationView).forType(javaType);
if (isUnicode) {
return objectReader.readValue(inputMessage.getBody());
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectReader.readValue(reader);
}
}
}
if (isUnicode) {
// 使用ObjectMapper从inputMessage中读取body中json字符串并转换成对象
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return this.objectMapper.readValue(reader, javaType);
}
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
}
}
3)判断能否写
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
// 1.判断是否支持传入的mediaType
if (!canWrite(mediaType)) {
return false;
}
if (mediaType != null && mediaType.getCharset() != null) {
Charset charset = mediaType.getCharset();
// 2.判断编码格式是否支持
if (!ENCODINGS.containsKey(charset.name())) {
return false;
}
}
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// 3.判断类能否序列化
if (this.objectMapper.canSerialize(clazz, causeRef)) {
return true;
}
logWarningIfNecessary(clazz, causeRef.get());
return false;
}
// AbstractHttpMessageConverter#canWrite
protected boolean canWrite(@Nullable MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
// 遍历支持的MediaType,判断传入的MediaType能否匹配
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
4)写数据到HttpOutputMessage
// AbstractHttpMessageConverter#write
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// 获取响应头
final HttpHeaders headers = outputMessage.getHeaders();
// 添加contentType响应头
addDefaultHeaders(headers, t, contentType);
if (outputMessage instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
@Override
public OutputStream getBody() {
return outputStream;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}));
}
else {
// 转换数据
writeInternal(t, outputMessage);
outputMessage.getBody().flush();
}
}
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// 从响应头content-type中获取MediaType
MediaType contentType = outputMessage.getHeaders().getContentType();
// 获取编码字符集
JsonEncoding encoding = getJsonEncoding(contentType);
OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
// 创建JSON生成器
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputStream, encoding);
try {
writePrefix(generator, object);
Object value = object;
Class<?> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
// 获取ObjectWriter
ObjectWriter objectWriter = (serializationView != null ?
this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
if (filters != null) {
objectWriter = objectWriter.with(filters);
}
if (javaType != null && javaType.isContainerType()) {
objectWriter = objectWriter.forType(javaType);
}
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
// 使用objectWriter转换value对象成json,并写入到HttpOutputMessage中
objectWriter.writeValue(generator, value);
writeSuffix(generator, object);
generator.flush();
generator.close();
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
}
}