Skip to content

ConversionService is inconsistently used in Spring Boot application #6222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
stevenschlansker opened this issue Jun 24, 2016 · 26 comments
Closed
Assignees

Comments

@stevenschlansker
Copy link

stevenschlansker commented Jun 24, 2016

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

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jun 24, 2016
@philwebb
Copy link
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.

@philwebb philwebb added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Jun 30, 2016
@stevenschlansker
Copy link
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...

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jul 1, 2016
@philwebb
Copy link
Member

philwebb commented Jul 2, 2016

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

@philwebb philwebb added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Jul 2, 2016
@stevenschlansker
Copy link
Author

stevenschlansker commented 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 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.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jul 5, 2016
@philwebb
Copy link
Member

philwebb commented Jul 5, 2016

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.

@philwebb philwebb added the for: team-attention An issue we'd like other members of the team to review label Jul 5, 2016
@stevenschlansker
Copy link
Author

That's a great improvement, although still wouldn't cover the case of trying to add your own non-default converters, only built-ins.

@philwebb
Copy link
Member

philwebb commented Jul 5, 2016

Thinking on this a bit more, it's not going to be possible to create the ConversionService as bean and use that ConversionService during bean creation. I think your best bet is to use an ApplicationContextInitializer to setup the ConversionService:

static class Initializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        // customize conversionService
        applicationContext.getEnvironment().setConversionService(conversionService);
    }

}

@stevenschlansker
Copy link
Author

stevenschlansker commented Jul 5, 2016

If that is the case, then I believe section 9.5.5 "Configuring a ConversionService" of the documentation is extremely confusing.

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#core-convert-Spring-config

A ConversionService is a stateless object designed to be instantiated at application startup, then shared between multiple threads. In a Spring application, you typically configure a ConversionService instance per Spring container (or ApplicationContext). That ConversionService will be picked up by Spring and then used whenever a type conversion needs to be performed by the framework. You may also inject this ConversionService into any of your beans and invoke it directly.

To register a default ConversionService with Spring, add the following bean definition with id conversionService:

This strongly implies that a conversion service registered as a bean will be used "whenever a type conversion needs to be performed by the framework", which clearly is not happening.

@philwebb
Copy link
Member

philwebb commented Jul 6, 2016

Indeed, that is quite confusing. It seems that the conversionService bean is applied to the BeanFactory but not the Environment.

@jhoeller
Copy link

jhoeller commented Jul 6, 2016

Indeed, I wonder whether we should simply also apply it to the Environment there in finishBeanFactoryInitialization. We can consider doing so for 4.3.2, in time for Boot 1.4 GA.

@philwebb
Copy link
Member

philwebb commented Jul 6, 2016

@jhoeller Perhaps with AbstractApplicationContext.createEnvironment() should also change the default to DefaultFormattingConversionService so that JSR-310 dates work out of the box? I think the reason that DefaultConversionService is because AbstractPropertyResolver is in spring-core and DefaultFormattingConversionService is in spring-context.

@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label Jul 6, 2016
@philwebb
Copy link
Member

philwebb commented Jul 6, 2016

We also need to be careful not to replace the Environment ConversionService if it has been replaced by the user already (for example using a ApplicationContextInitializer).

@jhoeller
Copy link

jhoeller commented Jul 6, 2016

DefaultFormattingConversionService is not really meant to be used at that level. If we believe that some further JSR-310 conversion is due at the DefaultConversionService level, we should rather consider adding those specific converters there instead of just in DefaultFormattingConversionService.

@jhoeller
Copy link

jhoeller commented Jul 6, 2016

The replacement part is where it gets tricky. We fundamentally don't know whether the Environment has been customized before. The Environment would somehow have to indicate to us whether setConversionService has been called on it.

@jhoeller
Copy link

jhoeller commented Jul 6, 2016

As for further JSR-310 support at the DefaultConversionService level, note that we have convention-based support for class-contained valueOf/of/from methods there, actually aligned with JSR-310 by design. However, for the purposes above, Duration just has a parse(CharSequence) method. We could extend our convention there to find such parse methods for String input as well, which would take us a long way... probably with no specific converters to be added for JSR-310 at all.

@philwebb
Copy link
Member

philwebb commented Jul 6, 2016

Why is DefaultFormattingConversionService not meant to be used at that level (with the Environment created for the ApplicationContext)?

@jhoeller
Copy link

jhoeller commented Jul 6, 2016

DefaultFormattingConversionService is primarily designed for input/output data binding, not for configuration purposes. That's why it has Formatter, Printer, Parser abstractions. It can nevertheless be set up as an application context's conversionService bean but it's just not a sensible default for a general application context in the core framework there, bringing in too much stuff (in particular a dependency on org.springframework.format and its whole load of formatting annotations). If you know that you're setting up a web app, configuring a DefaultFormattingConversionService as conversionService bean is a sensible enough choice though.

@ksperling
Copy link
Contributor

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 (via AbstractPropertyResolver) has/uses a DefaultConversionService internally, that service isn't actually used by the BeanFactory 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 a BeanFactoryPostProcessor similar to the OP to work around this by setting a DefaultConversionService 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 a conversionService bean doesn't help for these sort of issues because it gets registered way too late.)

@kflorence
Copy link

kflorence commented Dec 29, 2016

I am also running into a similar issue with a custom GenericConverter which is being used inside a Spring MVC controller on a @RequestParam (trying to add support for scala.Option). We are on Spring Boot 1.2.3 / Spring Framework 4.1.6 (admittedly a bit behind).

Through debugging, I've noticed that while my application is starting up, my converter is available as I would expect. However, once a request goes through to my Spring MVC controller, suddenly my converter is not there, which results in the following exception:

org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'scala.Option'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [scala.Option]: no matching editors or conversion strategy found
	at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:74) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:47) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:603) ~[spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:104) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:618) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:725) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:291) [spring-boot-actuator-1.2.3.RELEASE.jar:1.2.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:102) [spring-boot-actuator-1.2.3.RELEASE.jar:1.2.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90) [spring-boot-actuator-1.2.3.RELEASE.jar:1.2.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.20.jar:8.0.20]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]

This is the line I have my breakpoint on: https://github.com/spring-projects/spring-framework/blob/v4.1.6.RELEASE/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java#L170

Note that there is a DefaultFormattingConversionService available when the request goes through, it just doesn't contain my converter.

I've tried various things, including providing my Spring Application with a custom initializer (as suggested above) to no avail. I cannot seem to get access to my converter there. :rage4:

I must say, at first glance using ConversionService in some form seems really powerful and straight forward, but in practice it's highly confusing. There is virtually no documentation on customizing it in the Spring Boot docs and the Spring Framework docs seem misleading.

@philwebb
Copy link
Member

philwebb commented Dec 29, 2016

@kflorence You converters might be registered with the wrong conversion service. Try putting a
breakpoint on WebMvcConfigurationSupport.mvcConversionService() (that's the conversion service used by Spring MVC).

If you want to register converters with that service you can add a WebMvcConfigurer bean. Something like:

@Component
public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter {

	@Override
	public void addFormatters(FormatterRegistry registry) {
		registry.addConverter(...);
	}

}

@kflorence
Copy link

@philwebb Thanks for the quick reply -- aha! So that's where that conversion service is coming from. Out of curiosity, why does Spring MVC always create it's own conversion service instead of using an existing one (if one exists)? Also, it would be nice if there was some way of telling Spring Boot to automatically add my converter to this conversion service instead of the other one it automatically adds it to (the global default one?).

Anyways, I think this configuration will work for me, but my GenericConverter actually requires access to the conversion service internally so I will need to figure out how to provide it without getting a circular dependency 🤔

@kflorence
Copy link

Seems like there is some discussion of accessing the ConversionService inside of a converter here: https://jira.spring.io/browse/SPR-6415

@kflorence
Copy link

This seems to work:

@Component
class SpringContextListener extends ApplicationListener[ContextRefreshedEvent] with WrapAsScala {
  @Inject private[converter] var conversionService: GenericConversionService = _
  @Inject private[converter] var genericConverters: java.util.Set[GenericConverter] = _

  def onApplicationEvent(event: ContextRefreshedEvent): Unit = {
    genericConverters.foreach(conversionService.addConverter)
  }
}

Hallelujah! 😃

@wilkinsona wilkinsona removed the status: feedback-provided Feedback has been provided label Feb 8, 2017
@sdavids
Copy link

sdavids commented Apr 27, 2017

What we tried to do:

@ConfigurationProperties
public class ApplicationProperties {

  private Duration timeout;
  
  ...
  
}
timout=PT1M
Duration d = props.getTimeout();

queue.offer(..., d == null ? DEFAULT_TIMEOUT : d.getSeconds(), SECONDS);

@hanusto
Copy link

hanusto commented Aug 10, 2017

@jhoeller and @philwebb : I am solving similar issue. Spring Boot app with Java8 and JSR-303/JSR-349 Bean Validation. Input DTO object that represents request has OffsetDateTime field. This filed has to be annotated with @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) to support ISO date time.

I want to configure Spring Boot application globally and avoid to use @DateTimeFormat on each field. DefaultFormattingConversionService use DateTimeFormatterRegistrar but how can I specify ISO format? How can I configure org.springframework.format.datetime.standard.DateTimeFormatterRegistrar?

@mbhave mbhave added the for: team-attention An issue we'd like other members of the team to review label Jun 6, 2018
@philwebb philwebb self-assigned this Jun 6, 2018
@philwebb
Copy link
Member

philwebb commented Jun 6, 2018

Since Spring Boot 2.0 we now have an ApplicationConversionService which we plan to register by default in Spring Boot 2.1. I'm going to close this one in favor of #12148 which should, I hope, unify the way that application conversion happens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants