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;
}

这里返回的 HandlerMethodReturnValueHandlerRequestResponseBodyMethodProcessor

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

img

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));

img

默认有 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 是钩子方法,会调用 WebMvcConfigurerconfigureMessageConverters 方法

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

img

分别是:

  • org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration.StringHttpMessageConverterConfiguration#stringHttpMessageConverter
  • org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration#mappingJackson2HttpMessageConverter

addDefaultHttpMessageConverters 添加了 8 个默认 HttpMessageConverter

img

因此总共是 10 个

img

HttpMessageConverterMediaType 的关系

// 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;
        }
    }


}

对于测试代码来说,selectedMediaTypeapplication/jsonMappingJackson2HttpMessageConverter 对其进行处理,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;
}

这里提供了两个自定义切入点,configureMessageConvertersextendMessageConverters ,自定义 WebMvcConfigurer 然后重写对应方法,从代码中可以看到这两个方法的区别,如果 configureMessageConverters 中添加了 HttpMessageConverteraddDefaultHttpMessageConverters 默认的不会被添加,而 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);
}

img

这 27 个 HandlerMethodArgumentResolver 提供了对方法参数的注解和类型支持

参考:处理器方法

RequestResponseBodyMethodProcessor 支持 @RequestBody

RequestResponseBodyMethodProcessor#resolveArgument
    RequestResponseBodyMethodProcessor#readWithMessageConverters

这里同样用到了 RequestResponseBodyMethodProcessor 里的 messageConverters

同样调用了 selectObjectMapper 方法,可以为特定类型自定义 ObjectMapper

RequestResponseBodyMethodProcessor 中的 ContentNegotiationManager

private final ContentNegotiationManager contentNegotiationManager;
posted @ 2022-11-28 09:47  流星<。)#)))≦  阅读(123)  评论(0编辑  收藏  举报