ConversionService is inconsistently used in Spring Boot application
See original GitHub issueI apologize in advance for cross-posting, but I have not received useful help on Stack Overflow and I suspect that the issue I am facing is either a bug in Spring Boot or at least very poor documentation. Original context here:
Short version is, I would like to configure my ConversionService to understand new types (i.e. java.time.Duration
). Per the docs, I try to wire it up with:
@Configuration
public class ConversionServiceConfiguration {
private static final FormattingConversionService SERVICE = new DefaultFormattingConversionService();
static {
new DateTimeFormatterRegistrar().registerFormatters(SERVICE);
}
@Bean
public static ConversionService conversionService() {
return SERVICE;
}
}
but it keeps being ineffective:
> Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AttachClientRule': Unsatisfied dependency expressed through constructor parameter 0:
> Error creating bean with name 'MyServiceConfig': Unsatisfied dependency expressed through field 'maxWatchTime': 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;
The BeanFactory still has a DefaultConversionService
sticking around from before I set my own.
I can fix this one with one hack:
... implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.setConversionService(SERVICE);
}
}
and then I get a different problem, where again the Environment
doesn’t have it set either!
> Caused by: java.lang.IllegalArgumentException: Cannot convert value [PT15s] from source type [String] to target type [Duration]
> at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:94)
> at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:65)
> at org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:143)
> at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:546)
Time for another hack:
... implements EnvironmentAware {
@Override
public void setEnvironment(Environment environment) {
((AbstractEnvironment) environment).setConversionService(SERVICE);
}
}
And that fixes this problem.
But how long until I find the next place one of these icky DefaultConversionService
instances is lying around? How can I make Spring Boot actually use my custom one for everything without having to keep diving deep into the guts and find lingering problems?
Spring 4.3.0, Spring Boot 1.4.0M3
Issue Analytics
- State:
- Created 7 years ago
- Comments:26 (11 by maintainers)
I’m running into a similar issue with
ConversionService
where something as seemingly trivial as@Value("${classifications:}") Set<String> classifications;
doesn’t work as expected: It injects a set containing an empty string, rather than an empty set (unhelpfully they both toString to[]
so I was scratching my head there for an hour…).I tracked this down to the fact that even though
PropertySourcesPropertyResolver
(viaAbstractPropertyResolver
) has/uses aDefaultConversionService
internally, that service isn’t actually used by theBeanFactory
when it’s trying to convert that value. The bean factory seems to have no conversion service by default so falls back to some property editors that end up doing the wrong thing. In the end I used aBeanFactoryPostProcessor
similar to the OP to work around this by setting aDefaultConversionService
on the bean factory itself.It would be nice if the usage of
ConversionService
within the configuration machinery were more obvious (i.e. using the same instance in all places as far as possible, making it easy to customize) and better documented (e.g. the fact that simply defining aconversionService
bean doesn’t help for these sort of issues because it gets registered way too late.)As for further JSR-310 support at the
DefaultConversionService
level, note that we have convention-based support for class-containedvalueOf
/of
/from
methods there, actually aligned with JSR-310 by design. However, for the purposes above,Duration
just has aparse(CharSequence)
method. We could extend our convention there to find suchparse
methods forString
input as well, which would take us a long way… probably with no specific converters to be added for JSR-310 at all.