侧边栏壁纸
博主头像
soulballad博主等级

技术文章记录及总结

  • 累计撰写 169 篇文章
  • 累计创建 26 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

【源码分析-Spring Boot】-15.Spring Boot MessageConverter 执行和原理

soulballad
2020-07-30 / 0 评论 / 0 点赞 / 57 阅读 / 6,320 字
温馨提示:
本文最后更新于 2022-03-03,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Spring Boot MessageConverter:【从零开始学Spring Boot】-16.Spring Boot MessageConverter消息转

1.自定义MessageConverter执行流程

以 UserController#add1 为例,简单分析一下源码

在 AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters 中有这样一段

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    //...

    try {
        message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
		// 遍历所有的 messageConverters
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
            GenericHttpMessageConverter<?> genericConverter =
                (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
            // 执行 canRead 方法,判断是否可以支持当前的 Content-Type
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                (targetClass != null && converter.canRead(targetClass, contentType))) {					
                // 如果可以支持,判断是否有消息体
                if (message.hasBody()) {
                    // 解析前处理逻辑
                    HttpInputMessage msgToUse =
                        getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                    // 调用 read 方法解析消息内容
                    body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                            ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                    // 解析后处理逻辑
                    body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                }
                else {
                    // 消息体为空时处理逻辑
                    body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                }
                break;
            }
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }

    // ...

    return body;
}

这里需要遍历 messageConverters 来寻找一个合适的处理器,那么这里的 messageConverters 如何获取到自定义的 HTTPMessageConverter 呢?

其实,在项目启动的时候,自定义 HTTPMessageConverter 被加载到 applicationContext 中。RequestMappingHandlerAdapter 在初始化完成后,调用其 afterPropertiesSet

public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        // 获取参数处理的 resolvers
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

在 getDefaultArgumentResolvers 中有声明 RequestResponseBodyMethodProcessor 和 RequestPartMethodArgumentResolver,二者都需要调用 getMessageConverters 方法

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}

它的实现如下

public List<HttpMessageConverter<?>> getMessageConverters() {
    return this.messageConverters;
}

这里的 messageConverters 通过构造函数加入了一部分,也在 WebMvcAutoConfiguration 中进行了扩展。

2.自定义MessageConverter加载

在 spring boot 启动的时候,会加载到 WebMvcAutoConfiguration.EnableWebMvcConfiguration 中 requestMappingHandlerAdapter 方法,这个方法用来声明一个 RequestMappingHandlerAdapter 的 bean,它又通过调用 super.requestMappingHandlerAdapter 来进行实例化。

在 super.requestMappingHandlerAdapter 通过adapter.setMessageConverters(getMessageConverters()); 将 messageConverters 设置到 adapter 上,这里的 getMessageConverters 实现如下

protected final List<HttpMessageConverter<?>> getMessageConverters() {
    if (this.messageConverters == null) {
        this.messageConverters = new ArrayList<>();
        // 配置 messageConverters
        configureMessageConverters(this.messageConverters);
        if (this.messageConverters.isEmpty()) {
            // 如果messageConverters为空,加载默认的配置
            addDefaultHttpMessageConverters(this.messageConverters);
        }
        // 加载扩展的 messageConverter
        extendMessageConverters(this.messageConverters);
    }
    return this.messageConverters;
}

在 extendMessageConverters 中通过委派,调用 DelegatingWebMvcConfiguration 的 extendMessageConverters 来扩展 messageConverters,最终调用到 WebMvcConfigurer 的 extendMessageConverters 方法

public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    for (WebMvcConfigurer delegate : this.delegates) {
        delegate.extendMessageConverters(converters);
    }
}

而 WebMvcConfig 恰好是 WebMvcConfigurer 的实现类,重写了它的 extendMessageConverters 方法,所以自定义的 HTTPMessageConverter 被加载到 RequestMappingHandlerAdapter 中。

0

评论区