0

I want to use the Spring ConversionService with custom Converter implementations to convert values from Spring XML config.

I configure a bean through xml config like this:

<bean name="A1" class="com.example.MyClass">
    <constructor-arg name="time" value="10"/>
</bean>

The associated class:

import java.time.Duration;

public class MyClass {
    public MyClass(Duration time) {
        System.out.println("TIME: " + time);
    }
}

The converter that should perform the conversion is:

public class StringToDurationInSecondsConverter implements Converter<String, Duration> {
    @Override
    public Duration convert(String source) {
        int seconds = Integer.valueOf(source);
        return Duration.ofSeconds(seconds);
    }
}

The config looks like this:

@SpringBootApplication
public class Application {

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

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

    @Bean
    public StringToDurationInSecondsConverter stringToDurationInSecondsConverter() {
        return new StringToDurationInSecondsConverter();
    }
    // ...
}

The converters are injected as expected into conversionService method, so that the conversionService should be properly initialized. But the conversion of the parameter time in the xml config could not be performed. The error message is:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'A1' defined in resource loaded through SAX InputSource: Unsatisfied dependency expressed through constructor parameter 0: Could not convert argument value of type [java.lang.String] to required type [java.time.Duration]: Failed to convert value of type 'java.lang.String' to required type 'java.time.Duration'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.time.Duration': no matching editors or conversion strategy found

If I set a breakpoint into my convert method it is never reached. So it looks like the converter is never called.

Even If I inject the ConversionService directly to the (modified) MyClass and call the convert method within the constructor ...

public class MyClass {
    public MyClass(String time, ConversionService conversionService) {
        final Duration duration = conversionService.convert(time, Duration.class);
        System.out.println(duration);
    }
}

... I get the exception:

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

The core of the application is configured with Springs Java config, but there is some more "volatile" config that is read from a file by another bean (what works apart from the conversion).

Is it possible to use Spring's ConversionService with custom Converters to convert values in a Spring xml config? How?

deamon
  • 89,107
  • 111
  • 320
  • 448
  • it seems to be failing to create the bean instance of `A1`. what is the code for class `MyClass` and what is the type of `time` in that class. from the error stack it looks like `time` is of type `java.time.Duration` to which you are trying to assign 10 as string literal while bean creation. – madteapot Sep 22 '17 at 09:52
  • The type of `time` is `java.time.Duration`. And I'm indeed passing the String `"10"` to this constructor, but it should be converted to `Duration` in between. I've added my example class to the question. – deamon Sep 22 '17 at 10:46
  • I don't think that the way you are trying to use converterService is in correct context. The converterServices are for you to use in your code NOT in a way so spring automatically convert the argument types when instantiating a bean. Take a look at official documentation at https://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert – madteapot Sep 22 '17 at 12:11
  • But even If I inject the converter directly, no appropriate converter is found. – deamon Sep 22 '17 at 12:21
  • how are you registering your custom converter class `StringToDurationInSecondsConverter`? – madteapot Sep 22 '17 at 12:22
  • I register it with `@Bean` annotated factory method in my Spring Java config as you can see in the 4th code block above. – deamon Sep 22 '17 at 12:28
  • 1
    i don't think adding the converter to spring context is enough. You need to register it to the converterService refer to accepted answer on https://stackoverflow.com/questions/25628063/no-converter-found-capable-of-converting-from-type-java-lang-string-to-type-org and official doc https://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert-Spring-config – madteapot Sep 22 '17 at 12:33
  • Thanks, it works! I had read the section of the reference, but it seemed to me that using `ConversionServiceFactoryBean` would be enough as the first example suggests. – deamon Sep 22 '17 at 12:49

1 Answers1

0

Thanks to the comment of Sotirios Delimanolis (on another question) I came to the following solution:

@Bean
public ConversionService conversionService(Set<Converter<?, ?>> converters) {
    final ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
    factory.setConverters(converters);
    factory.afterPropertiesSet(); // necessary
    return factory.getObject();
}

Converting attribute values from XML works fine now.

deamon
  • 89,107
  • 111
  • 320
  • 448