Description
I 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
Activity
philwebb commentedon Jun 30, 2016
The
javax.time
types should be automatically detected and supported byDefaultFormattingConversionService
so directly callingDateTimeFormatterRegistrar
shouldn't be necessary.Do you have an example project that you can share to show the problem? Conversion services are used in multiple places, so having some code to look at help a lot.
stevenschlansker commentedon Jul 1, 2016
It's worth noting that we are explicitly trying to not use classspath scanning -- does your "automatically detected and supported" comment require that as a prerequisite? I've had so much trouble over the years with classpath scanning that we are trying to explicitly configure everything up front.
I will try to produce a self contained example next week, I ran out of time to do it this week...
philwebb commentedon Jul 2, 2016
@stevenschlansker No, it's not related to classpath scanning. See these lines or
DefaultFormattingConversionService
.stevenschlansker commentedon Jul 5, 2016
Thanks for the reference -- that seems to cover a very small subset of what https://github.com/spring-projects/spring-framework/blob/v4.3.0.RELEASE/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterRegistrar.java#L157-L194 provides. It only has
TimeZone
andCalendar
types, notDuration
and friends.Here is a small test case showing how confusing this situation is:
https://gist.github.com/stevenschlansker/c432a6360fe8897a9cd9af6cd16e4719
You'll note that registering a custom
conversionService
bean works fine for@Value
injection, but does not work for theEnvironment
unless you crack it open and set it manually. It also shows that the default configuration does not know about theDuration
type at all.philwebb commentedon Jul 5, 2016
Oh wow, my bad. You're totally right. By default the
StandardEnvironment
usesAbstractPropertyResolver
which creates aDefaultConversionService
and not aDefaultFormattingConversionService
.@jhoeller Is there any reason why
AbstractPropertyResolver
doesn't use theDefaultFormattingConversionService
? Perhaps we should in Boot.35 remaining items