Spring ConversionService, добавляющий преобразователи

Я искал проблему ниже, но не смог найти ответ.

Я хочу использовать службу преобразования Spring, написав свой собственный преобразователь, реализующий org.springframework.core.convert.converter.Converter.

Затем я добавляю свой собственный конвертер, как показано ниже:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean id="StringToLoggerConverter" class="com.citi.spring.converter.LoggerConverter" />
       </list>
   </property>
</bean>

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

Как я могу не переопределить convertService и добавить только свой собственный конвертер в список конвертеров, в то же время сохранив существующие?

Заранее спасибо.


person nikkatsa    schedule 08.10.2012    source источник


Ответы (9)


Для тех, кто наткнулся на это сейчас с помощью поиска Google или аналогичного более чем через 2 года после первоначальной публикации вопроса, добавление конвертеров стало намного проще с помощью Java Config: WebMvcConfigurerAdapter предоставляет метод addFormatters(FormatterRegistry), который можно использовать для указания дополнительных пользовательских конвертеров.

person Christopher    schedule 07.08.2014
comment
Посмотрите метод extendMessageConverters(List<HttpMessageConverter<?>> converters) в WebMvcConfigurerAdapter, чтобы добавить свой конвертер поверх конвертеров по умолчанию. - person Isuru; 10.01.2019

Экспериментируя с разными способами и даже в некоторых случаях следуя исходному коду Spring, я наткнулся на интересную вещь.

Единственный способ, которым я нашел использование convertService без переопределения существующих преобразователей моими пользовательскими, заключался в том, чтобы либо расширить, либо повторно реализовать convertService, вызывая метод afterPropertiesSet() суперкласса, чтобы зарегистрировать преобразователи по умолчанию, а затем добавить пользовательские.

Но даже если бы я использовал этот способ, во время выполнения я получал исключение, что для моих конкретных типов не был найден конвертер (например, из String в Logger).

Это вызвало мой интерес, и я проследил за исходным кодом Spring, чтобы узнать, почему, и я понял, что Spring пытается найти собственный конвертер, зарегистрированный в PropertyEditor. Я не уверен, почему это происходит. Я должен добавить, что мое приложение не использует Spring MVC, и, возможно, нужно как-то зарегистрировать convertService, а я этого не делал.

Наконец, я решил проблему с регистрацией пользовательского конвертера с помощью редактора свойств. Эту документацию можно рассматривать как справочную:

http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html

Мне было бы очень интересно узнать, почему Spring не находит мои зарегистрированные пользовательские конвертеры в реестре convertService (или, по крайней мере, почему Spring не просматривает этот реестр, чтобы найти пользовательские конвертеры). Я пропустил какую-либо конфигурацию?

person nikkatsa    schedule 09.10.2012

Вы также можете добавить его динамически, используя метод addConverter в вашем классе DefaultConversionService:

DefaultConversionService cs = new <YourClassThatInheritsFromDefaultConversionService or DefaultConversionService>();

cs.addConverter(new MyConverter());
person Christian Vielma    schedule 30.06.2014

С Spring> 4 больше нет необходимости реализовывать собственный производный от ConversionService. Инициализируйте его в аннотированном классе @Configuration следующим образом:

@Configuration
public class ConversionServiceProvider
{
    @Autowired
    private MyConverterImpl myConverter;

    @Bean
    public ConversionService getConversionService()
    {
        ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
        bean.setConverters( getConverters() );
        bean.afterPropertiesSet();
        ConversionService object = bean.getObject();
        return object;
    }

    private Set<Converter<?, ?>> getConverters()
    {
        Set<Converter<?, ?>> converters = new HashSet<Converter<?, ?>>();

        converters.add( myConverter );
        // add here more custom converters, either as spring bean references or directly instantiated

        return converters;
    }
}
person Heri    schedule 03.06.2015
comment
Вы можете внедрить все объекты определенного типа, в данном случае Converter, с помощью Spring: @Autowired Set<Converter>. - person deamon; 15.09.2017

Наткнулся на очень интересный способ сделать это в Stackoverflow - https://stackoverflow.com/a/12760579/204788

Использование функции под названием Объединение коллекций, вы можете сделать это:

<bean id="customConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" parent="conversionService">
    <property name="converters">
        <list merge="true">
            <bean id="StringToLoggerConverter" class="com.citi.spring.converter.LoggerConverter" />
       </list>
   </property>
</bean>
person Biju Kunjummen    schedule 08.10.2012
comment
Это решение, похоже, больше не работает: org.springframework.beans.NotWritablePropertyException: неверные свойства «преобразователи» класса компонента [org.springframework.core.convert.support.DefaultConversionService]: свойство «преобразователи» компонента недоступно для записи или имеет недопустимый метод установки. Соответствует ли тип параметра установщика типу возвращаемого значения геттера? - person user327961; 12.03.2021

Достаточно переопределить ConversionServiceFactoryBean.afterPropertiesSet() и установить там объект ConversionService для ваших конвертеров. Пусть ваши преобразователи реализуют некоторый интерфейс, который позволяет установить объект ConversionService, скажем, ConversionServiceAware. Единственная проблема заключается в том, чтобы получить доступ к зарегистрированным преобразователям, поэтому вам также придется переопределить метод setConverters().

public class MyFormattingConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {

    private Set<?> cachedConverters = new LinkedHashSet<>();

    @Override
    public void setConverters(Set<?> converters) {
        super.setConverters(converters);
        this.cachedConverters = new LinkedHashSet<>(converters);

    }

    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        FormattingConversionService conversionService = getObject();
        for (Object converter : cachedConverters) {
            if (converter instanceof ConversionServiceAware) {
                ((ConversionServiceAware) converter).setConversionService(conversionService);
            }
        }
    }
}
person Alexander    schedule 03.04.2014

Этот вариант у меня работает. Если вы используете конфигурацию Java, вы можете добавить свои преобразователи в существующие GenericConversionService

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html

Config methods may have an arbitrary name and any number of arguments; each of those arguments will be autowired with a matching bean in the Spring container. Bean property setter methods are effectively just a special case of such a general config method. Such config methods do not have to be public.

    @Configuration
    class MyConfig {

    @Autowired
    void conversionService(GenericConversionService genericConversionService) {
        genericConversionService.addConverter(String.class, UUID.class, UUID::fromString);
        genericConversionService.addConverter(String.class, DateTime.class, DateTime::parse);
        genericConversionService.addConverter(String.class, EnumState.class, EnumState::valueOf);
    }
}
person limon4ick    schedule 16.09.2019
comment
Прочтите stackoverflow.com/help/how-to-answer и улучшите свой ответ. - person jasie; 16.09.2019

Чтобы устранить любые циклические зависимости, выполните следующие шаги (Spring Boot v5.2.1):

Зарегистрируйте простой сервис преобразования

@Configuration
public class ConverterProvider {

    @Bean
    public ConversionService conversionService() {
        ConversionService conversionService = new GenericConversionService();
        return conversionService;
    }
}

Внедряйте свои пользовательские конвертеры

@Component
public class ConvertersInjection {

    @Autowired
    private GenericConversionService conversionService;

    @Autowired
    private void converters(Set<Converter> converters) {
        converters.forEach(conversionService::addConverter);
    }
}

Конвертер может даже автоматически подключить ваш сервис преобразования.

@Component
public class PushNotificationConverter implements Converter<PushNotificationMessages.PushNotification, GCM> {
    @Lazy
    @Autowired
    private ConversionService conversionService;

    @Override
    public GCM convert(PushNotificationMessages.PushNotification source) {
        GCM gcm = new GCM();
        if (source.hasContent()) {
            PushNotificationMessages.PushNotification.Content content = source.getContent();
            if (content.hasData()) {
                conversionService.convert(content.getData(), gcm.getData().getClass());
            } else if (content.hasNotification()) {
                conversionService.convert(content.getNotification(), gcm.getNotification().getClass());
            }
        }
        return gcm;
    }
}
person LionH    schedule 05.02.2020

написать пользовательский сервис преобразования

public class CustomerConversionServiceFactoryBean extends ConversionServiceFactoryBean {

    List<Converter<Object, Object>> converters = new ArrayList<>();

    public CustomerConversionServiceFactoryBean() {
        super();

    DefaultConversionService conversionservice = (DefaultConversionService)  super.getObject();

        for(int i=0;i<converters.size();i++){
            conversionservice.addConverter(converters.get(i));
        }
    }
}

затем измените свой xml

<bean id="conversionService"
    class="CustomerConversionServiceFactoryBean" >
    <property name="converters" >
        <list>
            <bean class="CustomConverter"  />
        </list>
    </property>
</bean>

Я думаю, это поможет вам...

person sgpalit    schedule 08.10.2012