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?

5
  • 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. Commented May 13, 2014 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? Commented May 13, 2014 at 16:11
  • A bean is a bean no matter how it is constructed. Commented May 13, 2014 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 ;) Commented May 14, 2014 at 6:30
  • Nevermind, I was way off. Sorry and reopened. Commented May 14, 2014 at 6:50

1 Answer 1

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;
     }
Sign up to request clarification or add additional context in comments.

2 Comments

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.
How do we implement this if we do not have serviceClassName during application startup?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.