Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 734
ares Avatar asked Apr 02 '18 12:04

ares


People also ask

Does getBean return new instance?

getBean. Return an instance, which may be shared or independent, of the specified bean. This method allows a Spring BeanFactory to be used as a replacement for the Singleton or Prototype design pattern.

Why Autowired is not working?

When @Autowired doesn't work. There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.

Can you compare bean factory with application context?

The ApplicationContext comes with advanced features, including several that are geared towards enterprise applications, while the BeanFactory comes with only basic features. Therefore, it's generally recommended to use the ApplicationContext, and we should use BeanFactory only when memory consumption is critical.

Can you Autowire byType when multiple beans?

2) byType autowiring mode It internally uses setter injection. In this case, it works fine because you have created an instance of B type. It doesn't matter that you have different bean name than reference name. But, if you have multiple bean of one type, it will not work and throw exception.


2 Answers

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());
  }

}
like image 175
Karol Dowbecki Avatar answered Oct 02 '22 17:10

Karol Dowbecki


You can do like this:

@Resource
@Lazy
private ExecutorService executorService;

It works.

like image 41
heixia Avatar answered Oct 02 '22 17:10

heixia