Fix @ConditionalOnBean with annotation early FactoryBean initialization

Update `OnBeanCondition` with a variant of `getBeanNamesForAnnotation`
that does not cause early `FactoryBean` initialization.

Fixes gh-38473
This commit is contained in:
Phillip Webb
2023-11-22 11:03:55 -08:00
parent e7aeeb87cf
commit bc504a8a03
2 changed files with 71 additions and 2 deletions

View File

@@ -28,11 +28,13 @@ import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -131,6 +133,19 @@ class ConditionalOnBeanTests {
});
}
@Test
void beanProducedByFactoryBeanIsConsideredWhenMatchingOnAnnotation2() {
this.contextRunner
.withUserConfiguration(EarlyInitializationFactoryBeanConfiguration.class,
EarlyInitializationOnAnnotationFactoryBeanConfiguration.class)
.run((context) -> {
assertThat(EarlyInitializationFactoryBeanConfiguration.calledWhenNoFrozen).as("calledWhenNoFrozen")
.isFalse();
assertThat(context).hasBean("bar");
assertThat(context).hasSingleBean(ExampleBean.class);
});
}
private void hasBarBean(AssertableApplicationContext context) {
assertThat(context).hasBean("bar");
assertThat(context.getBean("bar")).isEqualTo("bar");
@@ -352,6 +367,35 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class EarlyInitializationFactoryBeanConfiguration {
static boolean calledWhenNoFrozen;
@Bean
@TestAnnotation
static FactoryBean<?> exampleBeanFactoryBean(ApplicationContext applicationContext) {
// NOTE: must be static and return raw FactoryBean and not the subclass so
// Spring can't guess type
ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext)
.getBeanFactory();
calledWhenNoFrozen = calledWhenNoFrozen || !beanFactory.isConfigurationFrozen();
return new ExampleFactoryBean();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(annotation = TestAnnotation.class)
static class EarlyInitializationOnAnnotationFactoryBeanConfiguration {
@Bean
String bar() {
return "bar";
}
}
static class WithPropertyPlaceholderClassNameRegistrar implements ImportBeanDefinitionRegistrar {
@Override
@@ -518,7 +562,7 @@ class ConditionalOnBeanTests {
}
@Target(ElementType.TYPE)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface TestAnnotation {