spring-framework
spring-framework copied to clipboard
Adjus registration order of BeanDefinition declared by @Configuration
While I tried to customize an AutoConfiguration, but @ConditionalOnBean did not working, then I tried to find the problem through examples, source code, and the Internet, and used different configuration methods, and finally found that it was because of the registration order of BeanDefinition.
In my opinion, their registration order should be like this:
- Configuration
- beans annotated on Configuration (e.g.
@Importand@EnableConfigurationProperties) - (static) InnerConfiguration
- @Bean method
Then about ConfigurationClassParser, the (static) InnerConfiguration should be managed by the fields in ConfigurationClass of Configuration, and if the Conditional of Configuration is not matched, then InnerConfiguration should also be skipped, ConfigurationClassBeanDefinitionReader will be like:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
loadBeanDefinitionsFromInnerConfigurationClass(configClass.getInnerConfigurationClass()); // something like this
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
}
Let's take a step back. Instead of describing the implementation changes you want, can you explain what are you trying to achieve and why it doesn't work?
Let's take a step back. Instead of describing the implementation changes you want, can you explain what are you trying to achieve and why it doesn't work?
Let me simplify it first, please check the code below:
@ConditionalOnClass(FooClient.class)
@ConditionalOnMissingBean(FooService.class)
@EnableConfigurationProperties(FooProperties.class)
@Configuration(proxyBeanMethods = false)
public class FooServiceAutoConfiguration {
public FooServiceAutoConfiguration() {
}
@ConditionalOnBean(FooProperties.class)
@ConditionalOnMissingBean(FooClientFactory.class)
@Bean
public FooClientFactory fooClientFactory(FooProperties fooProperties) {
return new FooClientFactory(fooProperties);
}
@ConditionalOnBean({
FooProperties.class,
FooClientFactory.class
})
@ConditionalOnMissingBean(FooService.class)
@Bean
public FooService fooService(FooProperties fooProperties, FooClientFactory fooClientFactory) {
return new FooServiceImpl(fooProperties, fooClientFactory);
}
}
Its execution order is as follows:
- parse the
ConfigurationClassinstance ofFooServiceAutoConfigurationthroughConfigurationClassParser, and registeredBeanDefinition - load
BeanDefinitionbyConfigurationClassBeanDefinitionReader2.1 check ifFooServiceAutoConfigurationshouldSkip 2.2 registerBeanDefinitionForImportedConfigurationClass: annotation@Import2.3 loadBeanDefinitionsForBeanMethod: annotation@Bean2.4 loadBeanDefinitionsFromImportedResources 2.5 loadBeanDefinitionsFromRegistrars(renamed toloadBeanDefinitionsFromImportBeanDefinitionRegistrarsin 7.x): annotation@EnableConfigurationPropertieswill be handled here
But, in step 2.3 loadBeanDefinitionsForBeanMethod, while check the conditional of @ConditionalOnBean(FooProperties.class), it found that there is no bean(or BeanDefinitation) for type FooProperties, because the BeanDefinition of type FooProperties load in step 2.5 loadBeanDefinitionsFromRegistrars.
I knew that if I remove @ConditionalOnBean(...), then it's will work well, but I still think it is a little strange that it cannot create beans when the condition exists. Would you or someone in spring team be willing to tell me the reason for this design?