20220628 HttpMessageConverter
概述
用于在 HTTP 请求和响应之间进行转换的策略接口。
接口定义
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
GenericHttpMessageConverter
继承了 HttpMessageConverter
接口,支持泛型
RequestMappingHandlerAdapter
对返回值的处理中使用到 HttpMessageConverter
测试代码:
@RestController
public class MyController {
@RequestMapping("/first")
public MyBean1 first() {
return new MyBean1(1, "first", new Date());
}
}
RequestMappingHandlerAdapter
中包含字段:
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
private List<HttpMessageConverter<?>> messageConverters;
HandlerMethodReturnValueHandlerComposite
中包含字段:
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
对于测试代码来说,访问 @RequestMapping
方法时,直到处理返回值:
DispatcherServlet#doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
RequestMappingHandlerAdapter#handleInternal
mav = invokeHandlerMethod(request, response, handlerMethod);
ServletInvocableHandlerMethod#invokeAndHandle
HandlerMethodReturnValueHandlerComposite#handleReturnValue
HandlerMethodReturnValueHandlerComposite#selectHandler
RequestResponseBodyMethodProcessor#handleReturnValue
AbstractMessageConverterMethodProcessor#writeWithMessageConverters
// HandlerMethodReturnValueHandlerComposite#selectHandler
// 选择具体的 HandlerMethodReturnValueHandler
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
这里返回的 HandlerMethodReturnValueHandler
是 RequestResponseBodyMethodProcessor
HandlerMethodReturnValueHandler
用于处理从处理器方法调用返回的值的策略接口。
接口定义
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
Spring Boot 默认注册情况
HandlerMethodReturnValueHandlerComposite
中默认有 15 个 HandlerMethodReturnValueHandler
,创建时机是在创建 RequestMappingHandlerAdapter
bean 的初始化方法的生命周期
RequestMappingHandlerAdapter#afterPropertiesSet
RequestMappingHandlerAdapter#getDefaultReturnValueHandlers
RequestResponseBodyMethodProcessor
RequestResponseBodyMethodProcessor
包含字段 messageConverters
,值来自于 RequestMappingHandlerAdapter
中的 messageConverters
protected final List<HttpMessageConverter<?>> messageConverters;
将 RequestMappingHandlerAdapter
中的 messageConverters
赋值给 HandlerMethodReturnValueHandlerComposite
// RequestMappingHandlerAdapter#afterPropertiesSet
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultReturnValueHandlers
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
默认有 10 个 HttpMessageConverter
,这里有两个 MappingJackson2HttpMessageConverter
,一个是自动配置的,可以通过自定义 @Bean
替代,且排序默认在前,有操作空间,另一个是 addDefaultHttpMessageConverters
方法中添加的,没什么操作空间
RequestResponseBodyMethodProcessor
对 @ResponseBody
提供支持
// org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsReturnType
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
StringHttpMessageConverter
实现 HttpMessageConverter
接口
支持的媒体类型
text/plain
*/*
支持的返回类型是 String
如果处理器返回的是 String
,就会被这个类处理
MappingJackson2HttpMessageConverter
支持的媒体类型
application/json
application/*+json
支持所有返回类型
排序在 StringHttpMessageConverter
后
MappingJackson2XmlHttpMessageConverter
支持的媒体类型
application/xml
text/xml
application/*+xml
支持所有返回类型
需要引入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
RequestMappingHandlerAdapter
中的 messageConverters
值来源
在实例化 WebMvcAutoConfiguration$EnableWebMvcConfiguration
属性赋值时,因为 EnableWebMvcConfiguration
的父类 DelegatingWebMvcConfiguration
中存在方法
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
触发了 WebMvcAutoConfigurationAdapter
的实例化
WebMvcAutoConfigurationAdapter
只有一个构造函数,通过构造参数为属性 messageConvertersProvider
注入了 ObjectProvider<HttpMessageConverters> messageConvertersProvider
,因为注入的是 ObjectProvider
,所以懒加载
注入的是 Spring Boot 自动配置的一个 HttpMessageConverters
,会收集容器中所有的 HttpMessageConverter
// org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration#messageConverters
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
messageConvertersProvider
的加载时机
RequestMappingHandlerAdapter requestMappingHandlerAdapter
实例化时,
// WebMvcConfigurationSupport#requestMappingHandlerAdapter
adapter.setMessageConverters(getMessageConverters());
// org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getMessageConverters
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
configureMessageConverters
是钩子方法,会调用 WebMvcConfigurer
的 configureMessageConverters
方法
WebMvcAutoConfigurationAdapter
是自动配置的唯一 WebMvcConfigurer
,定义了 configureMessageConverters
方法
// WebMvcAutoConfigurationAdapter#configureMessageConverters
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider
.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}
这里触发了 HttpMessageConverters
bean 的实例化,构造函数中会调用 WebMvcConfigurationSupport#getMessageConverters
public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
addDefaultConverters ? getDefaultConverters() : Collections.emptyList());
combined = postProcessConverters(combined);
this.converters = Collections.unmodifiableList(combined);
}
又通过构造函数参数注入了容器中自动配置的 2 个 HttpMessageConverter
分别是:
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration.StringHttpMessageConverterConfiguration#stringHttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration#mappingJackson2HttpMessageConverter
addDefaultHttpMessageConverters
添加了 8 个默认 HttpMessageConverter
因此总共是 10 个
HttpMessageConverter
与 MediaType
的关系
// org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)
// 确定 selectedMediaType 是否在请求中已设置 Content-Type
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
selectedMediaType = contentType;
}
else {
// 根据请求返回可接受的媒体类型
acceptableTypes = getAcceptableMediaTypes(request);
// 这里返回支持该返回类型的所有 HttpMessageConverter#getSupportedMediaTypes
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
// 对上两个进行比较,返回可用的
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
// 确定 selectedMediaType
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
}
对于测试代码来说,selectedMediaType
为 application/json
,MappingJackson2HttpMessageConverter
对其进行处理,AbstractJackson2HttpMessageConverter#writeInternal
写出响应正文
自定义
自定义 HttpMessageConverter
添加 8 个默认 HttpMessageConverter
WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerAdapter
WebMvcConfigurationSupport#getMessageConverters
WebMvcConfigurationSupport#addDefaultHttpMessageConverters
// WebMvcConfigurationSupport#getMessageConverters
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
这里提供了两个自定义切入点,configureMessageConverters
和 extendMessageConverters
,自定义 WebMvcConfigurer
然后重写对应方法,从代码中可以看到这两个方法的区别,如果 configureMessageConverters
中添加了 HttpMessageConverter
,addDefaultHttpMessageConverters
默认的不会被添加,而 extendMessageConverters
不会有这个问题
自定义对于特定返回类型使用的 ObjectMapper
AbstractJackson2HttpMessageConverter
中存在字段
private Map<Class<?>, Map<MediaType, ObjectMapper>> objectMapperRegistrations;
且 selectObjectMapper
方法中给了自定义的机会
通过自定义 MappingJackson2HttpMessageConverter
,并调用 registerObjectMappersForType
为特定类型指定 ObjectMapper
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =
new MappingJackson2HttpMessageConverter(objectMapper);
ObjectMapper myObjectMapper = new ObjectMapper();
myObjectMapper.setDateFormat(new SimpleDateFormat("yyyy"));
mappingJackson2HttpMessageConverter.registerObjectMappersForType(MyBean1.class,
map -> map.put(MediaType.APPLICATION_JSON, myObjectMapper));
return mappingJackson2HttpMessageConverter;
}
HandlerMethodArgumentResolver
概述
用于在给定请求的上下文中将方法参数解析为参数值的策略接口。
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
对比 HandlerMethodReturnValueHandler
接口
对 @RequestBody
的处理
测试代码:
@RestController
public class MyController {
@RequestMapping("/first")
public String first(@RequestBody MyBean1 myBean1) {
return myBean1.toString();
}
}
代码跟踪:
DispatcherServlet#doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
RequestMappingHandlerAdapter#handleInternal
RequestMappingHandlerAdapter#invokeHandlerMethod
ServletInvocableHandlerMethod#invokeAndHandle
ServletInvocableHandlerMethod#invokeForRequest
ServletInvocableHandlerMethod#getMethodArgumentValues
this.resolvers.supportsParameter
args[i] = this.resolvers.resolveArgument
RequestMappingHandlerAdapter
中包含字段
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
private HandlerMethodArgumentResolverComposite argumentResolvers;
HandlerMethodArgumentResolverComposite
中包含字段
private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
将 RequestMappingHandlerAdapter
中的 customArgumentResolvers
赋值给 HandlerMethodArgumentResolverComposite
// RequestMappingHandlerAdapter#afterPropertiesSet
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
getDefaultArgumentResolvers
中添加了 27 个 HandlerMethodArgumentResolver
,其中包括 RequestResponseBodyMethodProcessor
RequestResponseBodyMethodProcessor
对 @RequestBody
提供支持
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
这 27 个 HandlerMethodArgumentResolver
提供了对方法参数的注解和类型支持
参考:处理器方法
RequestResponseBodyMethodProcessor
支持 @RequestBody
RequestResponseBodyMethodProcessor#resolveArgument
RequestResponseBodyMethodProcessor#readWithMessageConverters
这里同样用到了 RequestResponseBodyMethodProcessor
里的 messageConverters
同样调用了 selectObjectMapper
方法,可以为特定类型自定义 ObjectMapper
RequestResponseBodyMethodProcessor
中的 ContentNegotiationManager
private final ContentNegotiationManager contentNegotiationManager;