Consider factory beans when finding candidates
Previously, if a bean name was a factory dereference its definition would not be found. When the definition wasn't found it was assumed that the bean was an autowire candidate and a default candidate. If this, in fact, was not the case, @ConditionalOnMissingBean would not match when it should have done and @ConditionalOnBean would match when it should not had done. This commit updates the bean-based conditions to correctly consider factory beans so that whether or not they are a candidate can be evaluated correctly. Fixes gh-42970
This commit is contained in:
parent
4a9da78eb8
commit
2b3c93ffda
|
@ -36,6 +36,7 @@ import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
@ -496,12 +497,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
}
|
}
|
||||||
for (String beanName : beanNames) {
|
for (String beanName : beanNames) {
|
||||||
if (beanFactory instanceof ConfigurableListableBeanFactory clbf) {
|
if (beanFactory instanceof ConfigurableListableBeanFactory clbf) {
|
||||||
try {
|
result.put(beanName, getBeanDefinition(beanName, clbf));
|
||||||
result.put(beanName, clbf.getBeanDefinition(beanName));
|
|
||||||
}
|
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
|
||||||
result.put(beanName, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.put(beanName, null);
|
result.put(beanName, null);
|
||||||
|
@ -510,6 +506,18 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static BeanDefinition getBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
try {
|
||||||
|
return beanFactory.getBeanDefinition(beanName);
|
||||||
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
if (BeanFactoryUtils.isFactoryDereference(beanName)) {
|
||||||
|
return getBeanDefinition(BeanFactoryUtils.transformedBeanName(beanName), beanFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A search specification extracted from the underlying annotation.
|
* A search specification extracted from the underlying annotation.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -267,6 +267,14 @@ class ConditionalOnBeanTests {
|
||||||
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void conditionalOnBeanTypeIgnoresNotDefaultCandidateFactoryBean() {
|
||||||
|
this.contextRunner
|
||||||
|
.withUserConfiguration(NotDefaultCandidateFactoryBeanConfiguration.class,
|
||||||
|
OnBeanClassWithFactoryBeanConfiguration.class)
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void conditionalOnBeanNameMatchesNotDefaultCandidateBean() {
|
void conditionalOnBeanNameMatchesNotDefaultCandidateBean() {
|
||||||
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfiguration.class, OnBeanNameConfiguration.class)
|
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfiguration.class, OnBeanNameConfiguration.class)
|
||||||
|
@ -332,6 +340,17 @@ class ConditionalOnBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnBean(ExampleFactoryBean.class)
|
||||||
|
static class OnBeanClassWithFactoryBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
String bar() {
|
||||||
|
return "bar";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnBean(type = "java.lang.String")
|
@ConditionalOnBean(type = "java.lang.String")
|
||||||
static class OnBeanClassNameConfiguration {
|
static class OnBeanClassNameConfiguration {
|
||||||
|
@ -385,6 +404,16 @@ class ConditionalOnBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class NotDefaultCandidateFactoryBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean(defaultCandidate = false)
|
||||||
|
ExampleFactoryBean exampleBeanFactoryBean() {
|
||||||
|
return new ExampleFactoryBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml")
|
@ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml")
|
||||||
static class XmlConfiguration {
|
static class XmlConfiguration {
|
||||||
|
|
|
@ -160,7 +160,7 @@ class ConditionalOnMissingBeanTests {
|
||||||
@Test
|
@Test
|
||||||
void testOnMissingBeanConditionWithFactoryBean() {
|
void testOnMissingBeanConditionWithFactoryBean() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(FactoryBeanConfiguration.class, ConditionalOnFactoryBean.class,
|
.withUserConfiguration(FactoryBeanConfiguration.class, ConditionalOnMissingBeanProducedByFactoryBean.class,
|
||||||
PropertyPlaceholderAutoConfiguration.class)
|
PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ class ConditionalOnMissingBeanTests {
|
||||||
void testOnMissingBeanConditionWithComponentScannedFactoryBean() {
|
void testOnMissingBeanConditionWithComponentScannedFactoryBean() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(ComponentScannedFactoryBeanBeanMethodConfiguration.class,
|
.withUserConfiguration(ComponentScannedFactoryBeanBeanMethodConfiguration.class,
|
||||||
ConditionalOnFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ScanBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ScanBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ class ConditionalOnMissingBeanTests {
|
||||||
void testOnMissingBeanConditionWithComponentScannedFactoryBeanWithBeanMethodArguments() {
|
void testOnMissingBeanConditionWithComponentScannedFactoryBeanWithBeanMethodArguments() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(ComponentScannedFactoryBeanBeanMethodWithArgumentsConfiguration.class,
|
.withUserConfiguration(ComponentScannedFactoryBeanBeanMethodWithArgumentsConfiguration.class,
|
||||||
ConditionalOnFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ScanBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ScanBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ class ConditionalOnMissingBeanTests {
|
||||||
void testOnMissingBeanConditionWithFactoryBeanWithBeanMethodArguments() {
|
void testOnMissingBeanConditionWithFactoryBeanWithBeanMethodArguments() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(FactoryBeanWithBeanMethodArgumentsConfiguration.class,
|
.withUserConfiguration(FactoryBeanWithBeanMethodArgumentsConfiguration.class,
|
||||||
ConditionalOnFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.withPropertyValues("theValue=foo")
|
.withPropertyValues("theValue=foo")
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
@ -193,8 +193,8 @@ class ConditionalOnMissingBeanTests {
|
||||||
@Test
|
@Test
|
||||||
void testOnMissingBeanConditionWithConcreteFactoryBean() {
|
void testOnMissingBeanConditionWithConcreteFactoryBean() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(ConcreteFactoryBeanConfiguration.class, ConditionalOnFactoryBean.class,
|
.withUserConfiguration(ConcreteFactoryBeanConfiguration.class,
|
||||||
PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,16 +202,16 @@ class ConditionalOnMissingBeanTests {
|
||||||
void testOnMissingBeanConditionWithUnhelpfulFactoryBean() {
|
void testOnMissingBeanConditionWithUnhelpfulFactoryBean() {
|
||||||
// We could not tell that the FactoryBean would ultimately create an ExampleBean
|
// We could not tell that the FactoryBean would ultimately create an ExampleBean
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(UnhelpfulFactoryBeanConfiguration.class, ConditionalOnFactoryBean.class,
|
.withUserConfiguration(UnhelpfulFactoryBeanConfiguration.class,
|
||||||
PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context).getBeans(ExampleBean.class).hasSize(2));
|
.run((context) -> assertThat(context).getBeans(ExampleBean.class).hasSize(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testOnMissingBeanConditionWithRegisteredFactoryBean() {
|
void testOnMissingBeanConditionWithRegisteredFactoryBean() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(RegisteredFactoryBeanConfiguration.class, ConditionalOnFactoryBean.class,
|
.withUserConfiguration(RegisteredFactoryBeanConfiguration.class,
|
||||||
PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ class ConditionalOnMissingBeanTests {
|
||||||
void testOnMissingBeanConditionWithNonspecificFactoryBeanWithClassAttribute() {
|
void testOnMissingBeanConditionWithNonspecificFactoryBeanWithClassAttribute() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(NonspecificFactoryBeanClassAttributeConfiguration.class,
|
.withUserConfiguration(NonspecificFactoryBeanClassAttributeConfiguration.class,
|
||||||
ConditionalOnFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,15 +227,15 @@ class ConditionalOnMissingBeanTests {
|
||||||
void testOnMissingBeanConditionWithNonspecificFactoryBeanWithStringAttribute() {
|
void testOnMissingBeanConditionWithNonspecificFactoryBeanWithStringAttribute() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(NonspecificFactoryBeanStringAttributeConfiguration.class,
|
.withUserConfiguration(NonspecificFactoryBeanStringAttributeConfiguration.class,
|
||||||
ConditionalOnFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testOnMissingBeanConditionWithFactoryBeanInXml() {
|
void testOnMissingBeanConditionWithFactoryBeanInXml() {
|
||||||
this.contextRunner
|
this.contextRunner
|
||||||
.withUserConfiguration(FactoryBeanXmlConfiguration.class, ConditionalOnFactoryBean.class,
|
.withUserConfiguration(FactoryBeanXmlConfiguration.class,
|
||||||
PropertyPlaceholderAutoConfiguration.class)
|
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
|
||||||
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +377,15 @@ class ConditionalOnMissingBeanTests {
|
||||||
.run((context) -> assertThat(context).hasBean("bar"));
|
.run((context) -> assertThat(context).hasBean("bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void typeBasedMatchingIgnoresFactoryBeanThatIsNotDefaultCandidate() {
|
||||||
|
this.contextRunner
|
||||||
|
.withUserConfiguration(NotDefaultCandidateFactoryBeanConfiguration.class,
|
||||||
|
ConditionalOnMissingFactoryBean.class)
|
||||||
|
.run((context) -> assertThat(context).hasBean("&exampleFactoryBean")
|
||||||
|
.hasBean("&additionalExampleFactoryBean"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void nameBasedMatchingConsidersBeanThatIsNotDefaultCandidate() {
|
void nameBasedMatchingConsidersBeanThatIsNotDefaultCandidate() {
|
||||||
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfig.class, OnBeanNameConfiguration.class)
|
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfig.class, OnBeanNameConfiguration.class)
|
||||||
|
@ -447,7 +456,17 @@ class ConditionalOnMissingBeanTests {
|
||||||
static class FactoryBeanConfiguration {
|
static class FactoryBeanConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
FactoryBean<ExampleBean> exampleBeanFactoryBean() {
|
ExampleFactoryBean exampleBeanFactoryBean() {
|
||||||
|
return new ExampleFactoryBean("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class NotDefaultCandidateFactoryBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean(defaultCandidate = false)
|
||||||
|
ExampleFactoryBean exampleFactoryBean() {
|
||||||
return new ExampleFactoryBean("foo");
|
return new ExampleFactoryBean("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,7 +567,7 @@ class ConditionalOnMissingBeanTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class ConditionalOnFactoryBean {
|
static class ConditionalOnMissingBeanProducedByFactoryBean {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
|
@ -558,6 +577,17 @@ class ConditionalOnMissingBeanTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class ConditionalOnMissingFactoryBean {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
ExampleFactoryBean additionalExampleFactoryBean() {
|
||||||
|
return new ExampleFactoryBean("factory");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class ConditionalOnIgnoredSubclass {
|
static class ConditionalOnIgnoredSubclass {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue