Skip to content

ConversionService is inconsistently used in Spring Boot application #6222

Closed
@stevenschlansker

Description

@stevenschlansker

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:

http://stackoverflow.com/questions/37952166/spring-boot-test-case-doesnt-use-custom-conversion-service

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

philwebb commented on Jun 30, 2016

@philwebb
Member

The javax.time types should be automatically detected and supported by DefaultFormattingConversionService so directly calling DateTimeFormatterRegistrar 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

stevenschlansker commented on Jul 1, 2016

@stevenschlansker
Author

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

philwebb commented on Jul 2, 2016

@philwebb
Member

@stevenschlansker No, it's not related to classpath scanning. See these lines or DefaultFormattingConversionService.

stevenschlansker

stevenschlansker commented on Jul 5, 2016

@stevenschlansker
Author

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 and Calendar types, not Duration 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 the Environment unless you crack it open and set it manually. It also shows that the default configuration does not know about the Duration type at all.

philwebb

philwebb commented on Jul 5, 2016

@philwebb
Member

Oh wow, my bad. You're totally right. By default the StandardEnvironment uses AbstractPropertyResolver which creates a DefaultConversionService and not a DefaultFormattingConversionService.

@jhoeller Is there any reason why AbstractPropertyResolver doesn't use the DefaultFormattingConversionService? Perhaps we should in Boot.

35 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @stevenschlansker@kflorence@sdavids@philwebb@ksperling

      Issue actions

        ConversionService is inconsistently used in Spring Boot application · Issue #6222 · spring-projects/spring-boot