1

I want to create a ConversionService with custom converters.

Inspired by another question I've tried to create a ConversionService by creating a ConversionServiceFactoryBean and setting the converters on it:

@Bean
public ConversionService conversionService(
        Set<Converter<?, ?>> converters,
        ConversionServiceFactoryBean factory) {
    factory.setConverters(converters);
    return factory.getObject();
}

@Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean() {
    return new ConversionServiceFactoryBean();
}

The Spring documentation suggests more or less the same in XML:

<bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>

But configuring a ConversionService did not work in my case. Did not work means following error was thrown when i tried to use this autowired conversionService in my class;

ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.time.Duration]

I had to use a GenericConversionService and add my converters to it:

@Bean
public ConversionService conversionService(Set<Converter<?, ?>> converters) {
    final GenericConversionService conversionService = new GenericConversionService();
    converters.forEach(conversionService::addConverter);
    return conversionService;
}

Now it works, but I want to understand why it doesn't work with ConversionServiceFactoryBean.

Why doesn't it work with ConversionServiceFactoryBean and Java config? Why does it work with GenericConversionService?

madteapot
  • 2,208
  • 2
  • 19
  • 32
deamon
  • 89,107
  • 111
  • 320
  • 448
  • It's still unclear to me what _did not work_ means. Please extract the relevant parts of that link into your question here. Present a [mcve] that demonstrates whichever failure you're referring to. – Sotirios Delimanolis Sep 22 '17 at 14:30

1 Answers1

2

It is because you are trying to register the converter in your conversionService method after the ConversionServiceFactoryBean bean has been initialised i.e. afterPropertiesSet has already been called while constructing the factory bean. If you want to use the first method in your question the converter needs to be registered before afterPropertiesSet method gets called. so I would change your conversionService and conversionServiceFactoryBean methods as follows;

@Bean
public ConversionService conversionService(ConversionServiceFactoryBean factory) {
    return factory.getObject();
}

@Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean(Set<Converter<?, ?>> converters) {
    ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
    factory.setConverters(converters);
    return factory;
}

However separating these two methods serves no purpose as the instance of conversionService would have already been registered in spring context. So better to just merge those two methods as following;

Note the method name is changed from conversionServiceFactoryBean to conversionService. Without the name change Spring will throw an exception failing to convert the data as intended.

@Bean
public ConversionServiceFactoryBean conversionService(Set<Converter<?, ?>> converters) {
    ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
    factory.setConverters(converters);
    return factory;
}

The spring xml example you quoted above can be converted to java config as above in the single method.

Thirumalai Parthasarathi
  • 4,541
  • 1
  • 25
  • 43
madteapot
  • 2,208
  • 2
  • 19
  • 32
  • In your first proposed solution, the `ConversionService` `@Bean` method is useless. The `ConversionServiceFactoryBean` `@Bean` will already implicitly place a `ConversionService` bean in the context. Spring calls `FactoryBean#getObject` itself on factory beans. – Sotirios Delimanolis Sep 22 '17 at 14:43
  • And fixing that makes your second solution look unnecessary since Spring does all the work for us anyway. – Sotirios Delimanolis Sep 22 '17 at 14:46
  • @SotiriosDelimanolis thanks for comments. I've edited the answer accordingly. – madteapot Sep 22 '17 at 14:53
  • The solution can be simplified by calling `afterPropertiesSet` explicitly. See https://stackoverflow.com/a/46364314/238134 – deamon Sep 25 '17 at 07:52