diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index 631e011e94..25ef759f72 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -202,16 +202,10 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } protected final MatchResult getMatchingBeans(Spec spec) { + ConfigurableListableBeanFactory beanFactory = getSearchBeanFactory(spec); ClassLoader classLoader = spec.getContext().getClassLoader(); - ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory(); boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT; Set> parameterizedContainers = spec.getParameterizedContainers(); - if (spec.getStrategy() == SearchStrategy.ANCESTORS) { - BeanFactory parent = beanFactory.getParentBeanFactory(); - Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, - "Unable to use SearchStrategy.ANCESTORS"); - beanFactory = (ConfigurableListableBeanFactory) parent; - } MatchResult result = new MatchResult(); Set beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers); @@ -219,8 +213,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat Map typeMatchedDefinitions = getBeanDefinitionsForType(classLoader, considerHierarchy, beanFactory, type, parameterizedContainers); Set typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions, - (name, definition) -> isCandidate(name, definition, beansIgnoredByType) - && !ScopedProxyUtils.isScopedTarget(name)); + (name, definition) -> !ScopedProxyUtils.isScopedTarget(name) + && isCandidate(beanFactory, name, definition, beansIgnoredByType)); if (typeMatchedNames.isEmpty()) { result.recordUnmatchedType(type); } @@ -232,7 +226,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat Map annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(classLoader, beanFactory, annotation, considerHierarchy); Set annotationMatchedNames = matchedNamesFrom(annotationMatchedDefinitions, - (name, definition) -> isCandidate(name, definition, beansIgnoredByType)); + (name, definition) -> isCandidate(beanFactory, name, definition, beansIgnoredByType)); if (annotationMatchedNames.isEmpty()) { result.recordUnmatchedAnnotation(annotation); } @@ -252,6 +246,17 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return result; } + private ConfigurableListableBeanFactory getSearchBeanFactory(Spec spec) { + ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory(); + if (spec.getStrategy() == SearchStrategy.ANCESTORS) { + BeanFactory parent = beanFactory.getParentBeanFactory(); + Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, + "Unable to use SearchStrategy.ANCESTORS"); + beanFactory = (ConfigurableListableBeanFactory) parent; + } + return beanFactory; + } + private Set matchedNamesFrom(Map namedDefinitions, BiPredicate filter) { Set matchedNames = new LinkedHashSet<>(namedDefinitions.size()); @@ -263,9 +268,25 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return matchedNames; } - private boolean isCandidate(String name, BeanDefinition definition, Set ignoredBeans) { - return (!ignoredBeans.contains(name)) - && (definition == null || (definition.isAutowireCandidate() && isDefaultCandidate(definition))); + private boolean isCandidate(ConfigurableListableBeanFactory beanFactory, String name, BeanDefinition definition, + Set ignoredBeans) { + return (!ignoredBeans.contains(name)) && (definition == null + || isAutowireCandidate(beanFactory, name, definition) && isDefaultCandidate(definition)); + } + + private boolean isAutowireCandidate(ConfigurableListableBeanFactory beanFactory, String name, + BeanDefinition definition) { + return definition.isAutowireCandidate() || isScopeTargetAutowireCandidate(beanFactory, name); + } + + private boolean isScopeTargetAutowireCandidate(ConfigurableListableBeanFactory beanFactory, String name) { + try { + return ScopedProxyUtils.isScopedTarget(name) + && beanFactory.getBeanDefinition(ScopedProxyUtils.getOriginalBeanName(name)).isAutowireCandidate(); + } + catch (NoSuchBeanDefinitionException ex) { + return false; + } } private boolean isDefaultCandidate(BeanDefinition definition) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java index 61fadfec7e..d27d4af67e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java @@ -45,6 +45,9 @@ import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.context.support.SimpleThreadScope; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -124,6 +127,16 @@ class ConditionalOnMissingBeanTests { }); } + @Test + void testAnnotationOnMissingBeanConditionWithScopedProxy() { + this.contextRunner.withInitializer(this::registerScope) + .withUserConfiguration(ScopedExampleBeanConfiguration.class, OnAnnotationConfiguration.class) + .run((context) -> { + assertThat(context).doesNotHaveBean("bar"); + assertThat(context.getBean(ScopedExampleBean.class)).hasToString("test"); + }); + } + @Test void testAnnotationOnMissingBeanConditionWithEagerFactoryBean() { // Rigorous test for SPR-11069 @@ -407,6 +420,10 @@ class ConditionalOnMissingBeanTests { }; } + private void registerScope(ConfigurableApplicationContext applicationContext) { + applicationContext.getBeanFactory().registerScope("test", new TestScope()); + } + @Configuration(proxyBeanMethods = false) static class OnBeanInAncestorsConfiguration { @@ -665,6 +682,17 @@ class ConditionalOnMissingBeanTests { } + @Configuration(proxyBeanMethods = false) + @TestAnnotation + static class ScopedFooConfiguration { + + @Bean + String foo() { + return "foo"; + } + + } + @Configuration(proxyBeanMethods = false) static class NotAutowireCandidateConfig { @@ -717,6 +745,12 @@ class ConditionalOnMissingBeanTests { } + @Configuration(proxyBeanMethods = false) + @Import(ScopedExampleBean.class) + static class ScopedExampleBeanConfiguration { + + } + @Configuration(proxyBeanMethods = false) static class UnrelatedExampleBeanConfiguration { @@ -893,6 +927,15 @@ class ConditionalOnMissingBeanTests { } + @Scope(scopeName = "test", proxyMode = ScopedProxyMode.TARGET_CLASS) + static class ScopedExampleBean extends ExampleBean { + + ScopedExampleBean() { + super("test"); + } + + } + static class CustomExampleBean extends ExampleBean { CustomExampleBean() { @@ -931,4 +974,8 @@ class ConditionalOnMissingBeanTests { } + static class TestScope extends SimpleThreadScope { + + } + }