[spring-projects/spring-boot]改进 HttpMessageConverters javadoc

2024-04-17 699 views
7

我使用 spring-boot-starter-parent 1.5.17.RELEASE。

我创建我的配置扩展WebMvcConfigurerAdapter,并覆盖extendMessageConverters 方法。

我看到 List<HttpMessageConverter<?>> 有两个 MappingJackson2HttpMessageConverters 和两个 StringHttpMessageConverters。

@Configuration
public class TestConfig extends WebMvcConfigurerAdapter {

  @Override
  public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    System.out.println(converters);
  }
}

图像

我尝试将我的 TestDeserializer 注册到杰克逊。但这只是第一个生效的。 这是第一个。 图像 这是第二个。 图像

我想这可能是一个错误?如果不是bug,有人可以解释一下吗?

回答

2

我尝试调试原因,发现两个MappingJackson2HttpMessageConverters的生产顺序。

首先,在 中org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration,该mappingJackson2HttpMessageConverter方法创建一个新容器MappingJackson2HttpMessageConverter并注册到 Spring 容器。

@Configuration
class JacksonHttpMessageConvertersConfiguration {

    @Configuration
    @ConditionalOnClass(ObjectMapper.class)
    @ConditionalOnBean(ObjectMapper.class)
    @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true)
    protected static class MappingJackson2HttpMessageConverterConfiguration {

        @Bean
        @ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class, ignoredType = {
                "org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
                "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
        public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(
                ObjectMapper objectMapper) {
            return new MappingJackson2HttpMessageConverter(objectMapper);
        }

    }
//......
}

其次,在 中org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,该messageConverters方法创建一个HttpMessageConverters对象。

@Configuration
@ConditionalOnClass(HttpMessageConverter.class)
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class })
@Import({ JacksonHttpMessageConvertersConfiguration.class,
        GsonHttpMessageConvertersConfiguration.class })
public class HttpMessageConvertersAutoConfiguration {
    //......
    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters() {
        return new HttpMessageConverters((this.converters != null) ? this.converters
                : Collections.<HttpMessageConverter<?>>emptyList());
    }
    //......
}

构造函数参数 this.converters 有一个StringHttpMessageConverter对象和最后一个MappingJackson2HttpMessageConverter对象。

第三,在org.springframework.boot.autoconfigure.web.HttpMessageConverters构造函数中,执行getDefaultConverters方法得到一个具有新StringHttpMessageConverter对象和新MappingJackson2HttpMessageConverter对象的List。并且getCombinedConverters不删除重复的对象。

public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> {
    public HttpMessageConverters(boolean addDefaultConverters,
            Collection<HttpMessageConverter<?>> converters) {
        List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
                addDefaultConverters ? getDefaultConverters()
                        : Collections.<HttpMessageConverter<?>>emptyList());
        combined = postProcessConverters(combined);
        this.converters = Collections.unmodifiableList(combined);
    }
}

图像 图像

我想问题可能出在这个getCombinedConverters方法上。

    private List<HttpMessageConverter<?>> getCombinedConverters(
            Collection<HttpMessageConverter<?>> converters,
            List<HttpMessageConverter<?>> defaultConverters) {
        List<HttpMessageConverter<?>> combined = new ArrayList<HttpMessageConverter<?>>();
        List<HttpMessageConverter<?>> processing = new ArrayList<HttpMessageConverter<?>>(
                converters);
        for (HttpMessageConverter<?> defaultConverter : defaultConverters) {
            Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
            while (iterator.hasNext()) {
                HttpMessageConverter<?> candidate = iterator.next();
                if (isReplacement(defaultConverter, candidate)) {
                    combined.add(candidate);
                    iterator.remove();
                }
            }
            combined.add(defaultConverter);
            if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) {
                configurePartConverters(
                        (AllEncompassingFormHttpMessageConverter) defaultConverter,
                        converters);
            }
        }
        combined.addAll(0, processing);
        return combined;
    }

该方法使用isReplacement双循环的方法来检查重复的对象。但代码不会删除重复的对象。对于重复对象,首先与现有转换器合并,然后添加重复的默认转换器对象。

4

demo.zip 这是问题演示代码。

2

你好@liudaomanbu,

我认为这是设计使然,请参阅HttpMessageConvertersjavadoc:

/**
 * Create a new {@link HttpMessageConverters} instance with the specified converters.
 * @param addDefaultConverters if default converters should be added
 * @param converters converters to be added. Items are added just before any default
 * converter of the same type (or at the front of the list if no default converter is
 * found) The {@link #postProcessConverters(List)} method can be used for further
 * converter manipulation.
 */

甚至还有针对这种确切行为的测试。我仍然将其标记为引起团队注意,因为我想知道我们在这方面是否有需要改进的地方。

8

谢谢您的回答,我认为 HttpMessageConverters 上的 javadoc 应该得到修复。我的理解是,替换后,应该删除默认转换器。

但我对设计也有疑问。解析参数时,只有第一个匹配到的HttpMessageConverter才能解析。即使第一个匹配的 HttpMessageConverter 失败,其他匹配的 HttpMessageConverter 也没有机会解析参数。所以,我认为同一个类型的HttpMessageConverter有多个对象是没有意义的

0

谢谢您的回答。

9

HttpMessageConverter仅当较早的转换器始终匹配(truecanReador返回canWrite)并因此阻止调用后面的转换器时,这才有意义。情况并非总是如此。不同的MappingJackson2HttpMessageConverter实例可以配置不同的支持的媒体类型,甚至可以进行子类化以实现自定义canReadcanWrite行为。最初忽略了回退到默认转换器的需要,这是 #1293 中描述并修复的问题。

8

谢谢。

9

我们将改进 javadoc 的某些地方(也许还有参考文档)。

@liudaomanbu我不确定这对你的情况有帮助,看来你直接进行了调试并对当前的行为感到惊讶。您能否详细说明一下并解释一下您首先想要实现/配置的目标?您想配置特定的 Jackson 选项吗?如果您能告诉我们更多有关您的用例的信息,也许我们可以在这里找到更好的解决方案。

7

我一开始就调试了另一个错误。

但该错误与HttpMessageConverter和相关HttpMessageConverters。在我公司基于Spring Boot的框架中,一个@Configuration新的HttpMessageConverters对象,并且不添加HttpMessageConvertersSpring容器中现有的对象。

该错误使得在文档上配置 Jackson 的所有方法都无效。

修复bug后,我发现MappingJackson2HttpMessageConverter不是单例,也不是全部MappingJackson2HttpMessageConverters应用配置。

我担心这也是一个错误,而 javadoc 让我认为这确实是一个错误。

所以我继续调试。

您的回答对我很有帮助,谢谢

1

最后,虽然威尔金索纳的说法非常正确。但从我的角度来看,我使用 spring-boot-starter-parent 框架。然后框架创建两个MappingJackson2HttpMessageConverter对象,并且总是第一个被使用。我觉得这很奇怪,也有点令人费解。

7

Spring启动版本:2.1.4.RELEASE

我也面临类似的问题,配置的内容ObjectMapper没有被MappingJackson2HttpMessageConverter.

然后我尝试配置我自己的,MappingJackson2HttpMessageConverter因为这里的文档说我应该能够使用我自己的。

我进行了调试,发现 @liudaomanbu 存在同样的问题,我在容器中配置的不是 Spring Boot (MVC) 使用的。见下图(看object id)

这是 IoC 容器中的一个: 图像

这是 Spring MVC 将对象转换为响应的方法: 图像

4

@kevinjom 在本期中做出的澄清之后,我们相信一切都应该按照参考文档和 javadoc 中的描述进行工作。如果您认为情况并非如此,请打开一个新问题,其中包含一个最小示例,该示例重现您所看到的行为以及您期望看到的行为的描述。

6

@wilkinsona 感谢您的回复,我稍后可能会使用演示代码库创建一个新问题。谢谢

3

发现问题,是因为我们也在使用@EnableWebMvc,删除它后,对象映射器可以被转换器拾取。