Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom springboot autoconfiguration not detecting beans

I developed a custom Spring Boot autoconfiguration to ease working with a proprietary messaging library.

The main autoconfiguration class is essentially as follows:

@Configuration
@ConditionalOnClass({LibServer.class, LibClient.class})
@EnableConfigurationProperties(LibProperties.class)
public class LibAutoConfiguration {

    @Autowired
    LibProperties props;

    @Bean
    @ConditionalOnMissingBean(LibServer.class)
    public LibServer lbServ() {
        // create and configure a server object
    }

    @Bean
    @ConditionalOnMissingBean(LibClient.class)
    public LibClient lbClient() {
        //create and configure a client object
    }
}

It seems however that the conditional annotation is not detecting beans declared in the main @SpringBootApplication annotated class.

It only detects beans declared in separate @Configuration annotated classes.

That is to say if I place two @Bean annotated methods returning a LibServer and a LibClient object in the main class I end up with two LibServer and two LibClient objects (the autoconfigured ones and the explicitly declared ones) in the context.

Native spring boot autoconfigurations (such as DataSource one) can instead detect beans declared in the main class too (such as a @Bean annotated jdbcTemplate method).

How do I get proper bean detection even for beans declared in the main class?

Edit

A complete multimodule maven project exhibiting the behaviour is at https://github.com/AlexFalappa/spring-boot-testcase

like image 862
Alessandro Falappa Avatar asked Mar 13 '16 15:03

Alessandro Falappa


People also ask

How do I enable specific auto-configuration in Spring boot?

Auto-Configuration in Spring BootThe annotation @EnableAutoConfiguration is used to enable the auto-configuration feature. The @EnableAutoConfiguration annotation enables the auto-configuration of Spring ApplicationContext by scanning the classpath components and registering the beans.

How do I register a custom auto-configuration?

In order to create a custom auto-configuration, we need to create a class annotated as @Configuration and register it. Let's create a custom configuration for a MySQL data source: @Configuration public class MySQLAutoconfiguration { //... } Next, we need to register the class as an auto-configuration candidate.

What is the difference between @configuration and EnableAutoConfiguration?

How They Differ. The main difference between these annotations is that @ComponentScan scans for Spring components while @EnableAutoConfiguration is used for auto-configuring beans present in the classpath in Spring Boot applications.

How do I create a custom bean in Spring boot?

Different Methods to Create a Spring BeanCreating Bean Inside an XML Configuration File (beans. xml) Using @Component Annotation. Using @Bean Annotation.


1 Answers

If you set the log level on debug in you application.properties (logging.level.org.springframework=DEBUG), you will notice that Spring will detect both definitions. However you will also see that the order in which this happens may not be what you expected, because it instantiate beans from the library configuration first, and AFTERWARDS from your main class , and thus you get 2 instances (stripped timestamps to make it friendlier):

Bean definitions

o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'autoConfigurationReport'
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.spring.boot.libbo.LibAutoConfiguration.lbServ()
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'org.springframework.boot.autoconfigure.condition.BeanTypeRegistry'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'autoConfigurationReport'
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.spring.boot.libbo.LibAutoConfiguration.lbClient()
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.DemoLibboApplication.libServ()
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.DemoLibboApplication.libClient()

Bean instantiation

o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'libAutoConfiguration'
Autoconfiguring LibServer
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'lbServ' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'libAutoConfiguration'
Autoconfiguring LibClient
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'lbClient' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'lib.CONFIGURATION_PROPERTIES'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store'
o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'demoLibboApplication'
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'libServ' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'libClient'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'libClient'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'demoLibboApplication'
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'libClient' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'libClient'

You can also see in the AUTO-CONFIGURATION REPORT that in your current implementation when the conditionals in the LibAutoConfiguration are evaluated, they match and normally it creates the beans:

Positive matches:
-----------------
...
LibAutoConfiguration#lbClient matched
  - @ConditionalOnMissingBean (types: af.libbo.LibClient; SearchStrategy: all) found no beans (OnBeanCondition)

LibAutoConfiguration#lbServ matched
  - @ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found no beans (OnBeanCondition)
...

However, if you add the same condition to your main class, you'll see that it will create the beans according to the definitions in LibAutoConfiguration, and when trying to create those for DemoLibboApplication, it will actually find the previously created beans and skip the instantiation:

Negative matches:
-----------------
...
DemoLibboApplication#libClient did not match
  - @ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found the following [lbServ] (OnBeanCondition)

DemoLibboApplication#libServ did not match
  - @ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found the following [lbServ] (OnBeanCondition)
...
like image 86
Morfic Avatar answered Nov 16 '22 00:11

Morfic