2

My situation is that the class name of a bean can be configured by an external properties-file. It's fine to do this with XML

  <bean id="myService" class="${app.serviceClass}" />

and properties-file

app.serviceClass=com.example.GreatestClassThereIs

I tried to convert it to Java using a BeanFactoryPostProcessor:

@Configuration
public class MyConfiguration implements BeanFactoryPostProcessor {

  @Value("${app.serviceClass}")
  private String serviceClassName;

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    logger.warn("let's register bean of class " + serviceClassName + "...");
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClassName(serviceClassName);

    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    registry.registerBeanDefinition("myService", beanDefinition);
    logger.warn("done " + beanDefinition);
  }
}

The problem is that the lifecycle of Spring hasn't yet set handled the @Value and serviceClassName is null. How do I get the property in there?

sjngm
  • 12,423
  • 14
  • 84
  • 114
  • Not sure what you are trying to achieve. You have a `@Configuration` class which is actually a `BeanFactoryPostProcessor`. Seems strange to me. Defining a `@Bean` and use the Spring `ClassUtils` to create an instance of that bean. Else registering a `RootBeanDefinition` with `${app.serviceClass}` might also work. – M. Deinum May 13 '14 at 14:33
  • @M.Deinum Well, the source in my question is just the important stuff for this question. I don't want to write off-topic stuff that may cause more confusion. I will check your two suggestions. Would both cause Spring to do DI and such on the bean? – sjngm May 13 '14 at 16:11
  • A bean is a bean no matter how it is constructed. – M. Deinum May 13 '14 at 17:35
  • @SotiriosDelimanolis I completely fail to understand what the supposedly duplicate question is about. I can see that an answer suggests to use `BeanDefinitionRegistryPostProcessor`, which I haven't heard of yet, but the rest is so confusing I can't even argue whether or not my question is a duplicate ;) – sjngm May 14 '14 at 06:30
  • Nevermind, I was way off. Sorry and reopened. – Sotirios Delimanolis May 14 '14 at 06:50

1 Answers1

4

Why not simply define a new Bean in your ApplicationContext by using Class.forName() from the @Value injected into the @Configuration?

So something like this:

@Configuration
public class MyConfiguration
{

     @Value("${app.serviceClass}")
     private String serviceClassName;

     @Bean
     public Object myService()
     {
          return Class.forName(serviceClassName).newInstance();
     }
}

EDIT by sjngm (for better readability than in the comment):

     @Bean
     public MyInterface myService()
     {
        Class<?> serviceClass = Class.forName(serviceClassName);
        MyInterface service = MyInterface.class.cast(serviceClass.newInstance());
        return service;
     }
sjngm
  • 12,423
  • 14
  • 84
  • 114
Rob Lockwood-Blake
  • 4,688
  • 24
  • 22
  • I guess that when implementing against interfaces the return type of `myService` can be the interface. This is important for DI of this bean in other places. – sjngm May 14 '14 at 06:33
  • 1
    How do we implement this if we do not have serviceClassName during application startup? – Sushant Gupta Aug 27 '18 at 06:27