Search code examples
javaspringspring-bootautowired

spring - ApplicationContext registerBean autowiring fails but getBean works in Spring 5


I'm using a configuration class that uses dynamic bean registration:

@Configuration
public class ConfigClass {

    @Autowired
    private GenericApplicationContext applicationContext;

    @PostConstruct
    private void init() {
        System.out.println("init");
        applicationContext.registerBean("exService", ExecutorService.class, () -> Executors.newFixedThreadPool(10), bd -> bd.setAutowireCandidate(true));
        System.out.println("init done");
    }
}

If I try to autowire the bean, application startup fails with error Field exService in com.example.DemoApplication required a bean of type 'java.util.concurrent.ExecutorService' that could not be found.

From the logs I can see that the init method on config class wasn't called before the error as the two system out statements were not printed out.

However, when I use applicationContext.getBean(ExecutorService.class) it does work without any issues.

Anyway I can get the bean to Autowire?

I'm deliberately not using the @Bean annotation because I need to register the beans dynamically based on certain conditions.


Solution

  • It could be because you are registering your bean in the middle of the context initialization phase. If your target bean initializes and auto-wires ExecutorService before ConfigClass @PostConstruct is invoked there simply is no bean available.

    You can try forcing the initialization order:

    @Component
    @DependsOn("configClass")
    public class MyComponent
    
      @Autowired
      private ExecutorService executorService;
    

    However it would be cleaner to register a bean definition using BeanFactoryPostProcessor with BeanDefinitionBuilder:

    @Component
    public class MyBeanRegistration implements BeanFactoryPostProcessor {
    
      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
        BeanDefinitionRegistry reg = (BeanDefinitionRegistry) bf;
        reg.registerBeanDefinition("exService",
          BeanDefinitionBuilder
            .rootBeanDefinition(ExecutorService.class)
            .setFactoryMethod("newWorkStealingPool")
            .getBeanDefinition());
      }
    
    }