2

I want to create a Spring ConversionService with custom Converters, but the return value of ConversionServiceFactoryBean#getObject is null. See example:

@Bean
@Autowired
public ConversionService conversionService(Set<Converter<?, ?>> converters) {
    final ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
    factory.setConverters(converters);
    return checkNotNull(
            factory.getObject(),
            "conversionService must not be null.");
}

checkNotNull throws a NullPointerException. The converters are injected as expected. Why does the factory return null? How can I fix that?

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
deamon
  • 89,107
  • 111
  • 320
  • 448
  • 1
    since you are using new keyword to create the factory bean and keeping it local , its not present in Spring context. Try creating a method (annotated with @Bean) returning factory bean and take it as parm to conversionService and then do getObject – Goro Sep 22 '17 at 11:50
  • Thanks, that's it! I've created a full answer to help others with same issue. – deamon Sep 22 '17 at 12:01
  • 1
    It's not because it's not in the Spring context. That's irrelevant. You can create and manage the instance yourself just fine. But you have to follow the requirements of the `FactoryBean` and `InitializingBean` interfaces and call `afterPropertiesSet` before `getObject`. – Sotirios Delimanolis Sep 22 '17 at 13:50
  • Thank you. I wasn't aware of `afterPropertiesSet`. I hadn't expected this error-prone pattern, but now I know. – deamon Sep 25 '17 at 07:38

1 Answers1

3

Thanks to the comment of Sotirios Delimanolis I came to the following solution:

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

This is essentially a shorthand for the following configuration:

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

The factory remains in an unfinished state until afterPropertiesSet (explanation) is called. However, one doesn't need to call it, if the ConversionServiceFactoryBean itself is returned instead of the ConversionService. Since the factory is a InitializingBean and a FactoryBean Spring will call afterPropertiesSet and getObject internally, if a ConversionService instance is needed.

deamon
  • 89,107
  • 111
  • 320
  • 448
  • 1
    The `ConversionServiceFactoryBean` is already producing a `ConversionService` bean, there's no need for another. Just pass the converters to the `ConversionServiceFactoryBean` and get rid of the `conversionService` `@Bean` method. – Sotirios Delimanolis Sep 22 '17 at 13:55
  • Thanks, I've changed and simplified the solution. – deamon Sep 25 '17 at 07:52
  • 1
    You can simplify it further. `ConversionServiceFactoryBean` is both a an `InitializingBean` and a `FactoryBean`. Both of those interface are special to Spring and part of a bean's lifecycle. Spring will invoke `afterPropertiesSet` and `getObject` for you if you register a bean of that type, putting the result in the application context as well. So the easiest way to do this would just be to instantiate `ConversionServiceFactoryBean`, set its converters, and return it. The rest is done by Spring. – Sotirios Delimanolis Sep 25 '17 at 14:14
  • Thank you very much! I've learned something fundamental today (worth a downvote ;-) ). – deamon Sep 25 '17 at 14:49