Дублировать преобразователи сообщений в RestTemplateBuilder?

Я использую spring-boot с spring-web и jackson.

Проблема: когда RestTemplate автоматически инициализируется Spring, конструктор получает дубликат MessageConverters:

org.springframework.http.converter.ByteArrayHttpMessageConverter@6a1b4854,
org.springframework.http.converter.StringHttpMessageConverter@2d5b549b, 
org.springframework.http.converter.StringHttpMessageConverter@6a175162, 
org.springframework.http.converter.ResourceHttpMessageConverter@7641c4e7, 
org.springframework.http.converter.ResourceRegionHttpMessageConverter@650a0b50, 
org.springframework.http.converter.xml.SourceHttpMessageConverter@55e3b64d, 
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@52f71d2, 
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@f3c27e9, 
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@7d31fb6c, 
org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter@701c413, 
org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter@48543f11

Видите ли, есть 3 дубликата:

StringHttpMessageConverter
MappingJackson2HttpMessageConverter
MappingJackson2XmlHttpMessageConverter

Поскольку я сам не инициализирую преобразователи сообщений: почему контекст приложения вообще содержит повторяющиеся преобразователи, которые затем добавляются в шаблон resttemplate?

Особенно: не сбивает ли это с толку (де) сериализацию, если некоторые конвертеры повторяются (но с другой конфигурацией)?

Например: ObjectMapper первого MappingJackson2HttpMessageConverter содержит больше registeredModuleTypes[Jdk8Module, JavaTimeModule, ParamterNamesModule, JsonComponentModule, GeoModule], чем 2-й (который содержит только: [Jdk8Module, JavaTimeModule]).

Имеет ли это смысл?

Он создается через RestTemplateAutoConfiguration.restTemplateBuilder(), там уже есть все дубликаты MessageConverters.

Не могли бы вы установить точку останова в конструкторе и опубликовать весь стек вызовов? Я хочу увидеть, откуда он создается.

LppEdd 04.04.2019 16:03

Это происходит от RestTemplateAutoConfiguration.restTemplateBuilder(). Таким образом, он просто вводит любой HttpMessageConverters, найденный в пути к классам. Вопрос в том, почему могут быть дубликаты?

membersound 04.04.2019 16:23

Ну вот и все, что мне удалось получить.

LppEdd 04.04.2019 16:54

Добавлено объяснение того, как Spring выбирает HttpMessageConverter. См. внизу.

LppEdd 04.04.2019 17:06
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
4
1 037
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Виновник здесь, в HttpMessageConverters

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

В частности, эта строка (отформатированная)

List<HttpMessageConverter<?>> combined = 
       getCombinedConverters(
           converters, 
           addDefaultConverters 
               ? getDefaultConverters() 
               : Collections.emptyList());

Коллекция converters содержит отсканированные HttpMessageConverter(и).
На основе среды.

Затем этот список объединяется со списком По умолчанию, предоставленным WebMvcConfigurationSupport.

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
    private static final boolean romePresent;
    private static final boolean jaxb2Present;
    private static final boolean jackson2Present;
    private static final boolean jackson2XmlPresent;
    private static final boolean jackson2SmilePresent;
    private static final boolean jackson2CborPresent;
    private static final boolean gsonPresent;
    private static final boolean jsonbPresent;
    ...

Фактическая документация для WebMvcConfigurationSupport состояний

This class registers ... ... a range of HttpMessageConverters depending on the third-party libraries available on the classpath.

Отсканированные HttpMessageConverter(s) находятся и создаются через HttpMessageConvertersAutoConfiguration, документация которого находится

Auto-configuration for HttpMessageConverters.

Этот класс сам по себе предоставляет StringHttpMessageConverter

@Bean
@ConditionalOnMissingBean
public StringHttpMessageConverter stringHttpMessageConverter() {
    StringHttpMessageConverter converter = new StringHttpMessageConverter(
            this.properties.getCharset());
    converter.setWriteAcceptCharset(false);
    return converter;
}

Затем он импортирует автоконфигурации Джексона или Гсона.

@Import({ 
    JacksonHttpMessageConvertersConfiguration.class
    GsonHttpMessageConvertersConfiguration.class,
    JsonbHttpMessageConvertersConfiguration.class 
})

И вот как эти основанные на среде «суммируются» с предопределенными.


Spring не путается с дубликатами, потому что он просто берет первый совместимый. Посмотрите, как выбирается HttpMessageConverter

Вы можете видеть, что это всего лишь простой цикл for, и каждого новообращенного просят сказать: «Могу ли я сделать это?» методом canWrite

Выбирается первый действительный.

tyvm за подробное понимание!

membersound 04.04.2019 17:08

@membersound, нет проблем. Это было весело. Если вам нужны другие разъяснения, дайте мне знать.

LppEdd 04.04.2019 17:19

Другие вопросы по теме