Skip to content

Commit 86b0c76

Browse files
committed
Fix annotation matching when using scoped proxies
Update `OnBeanCondition` to check `isAutowireCandidate` on the original bean of scoped proxy targets. Fixes gh-43423
1 parent 8ca8ab1 commit 86b0c76

File tree

2 files changed

+81
-13
lines changed

2 files changed

+81
-13
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -202,25 +202,19 @@ private ConditionOutcome evaluateConditionalOnMissingBean(Spec<ConditionalOnMiss
202202
}
203203

204204
protected final MatchResult getMatchingBeans(Spec<?> spec) {
205+
ConfigurableListableBeanFactory beanFactory = getSearchBeanFactory(spec);
205206
ClassLoader classLoader = spec.getContext().getClassLoader();
206-
ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory();
207207
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
208208
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
209-
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
210-
BeanFactory parent = beanFactory.getParentBeanFactory();
211-
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
212-
"Unable to use SearchStrategy.ANCESTORS");
213-
beanFactory = (ConfigurableListableBeanFactory) parent;
214-
}
215209
MatchResult result = new MatchResult();
216210
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
217211
spec.getIgnoredTypes(), parameterizedContainers);
218212
for (String type : spec.getTypes()) {
219213
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(classLoader,
220214
considerHierarchy, beanFactory, type, parameterizedContainers);
221215
Set<String> typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions,
222-
(name, definition) -> isCandidate(name, definition, beansIgnoredByType)
223-
&& !ScopedProxyUtils.isScopedTarget(name));
216+
(name, definition) -> !ScopedProxyUtils.isScopedTarget(name)
217+
&& isCandidate(beanFactory, name, definition, beansIgnoredByType));
224218
if (typeMatchedNames.isEmpty()) {
225219
result.recordUnmatchedType(type);
226220
}
@@ -232,7 +226,7 @@ protected final MatchResult getMatchingBeans(Spec<?> spec) {
232226
Map<String, BeanDefinition> annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(classLoader,
233227
beanFactory, annotation, considerHierarchy);
234228
Set<String> annotationMatchedNames = matchedNamesFrom(annotationMatchedDefinitions,
235-
(name, definition) -> isCandidate(name, definition, beansIgnoredByType));
229+
(name, definition) -> isCandidate(beanFactory, name, definition, beansIgnoredByType));
236230
if (annotationMatchedNames.isEmpty()) {
237231
result.recordUnmatchedAnnotation(annotation);
238232
}
@@ -252,6 +246,17 @@ protected final MatchResult getMatchingBeans(Spec<?> spec) {
252246
return result;
253247
}
254248

249+
private ConfigurableListableBeanFactory getSearchBeanFactory(Spec<?> spec) {
250+
ConfigurableListableBeanFactory beanFactory = spec.getContext().getBeanFactory();
251+
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
252+
BeanFactory parent = beanFactory.getParentBeanFactory();
253+
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
254+
"Unable to use SearchStrategy.ANCESTORS");
255+
beanFactory = (ConfigurableListableBeanFactory) parent;
256+
}
257+
return beanFactory;
258+
}
259+
255260
private Set<String> matchedNamesFrom(Map<String, BeanDefinition> namedDefinitions,
256261
BiPredicate<String, BeanDefinition> filter) {
257262
Set<String> matchedNames = new LinkedHashSet<>(namedDefinitions.size());
@@ -263,9 +268,25 @@ private Set<String> matchedNamesFrom(Map<String, BeanDefinition> namedDefinition
263268
return matchedNames;
264269
}
265270

266-
private boolean isCandidate(String name, BeanDefinition definition, Set<String> ignoredBeans) {
267-
return (!ignoredBeans.contains(name))
268-
&& (definition == null || (definition.isAutowireCandidate() && isDefaultCandidate(definition)));
271+
private boolean isCandidate(ConfigurableListableBeanFactory beanFactory, String name, BeanDefinition definition,
272+
Set<String> ignoredBeans) {
273+
return (!ignoredBeans.contains(name)) && (definition == null
274+
|| isAutowireCandidate(beanFactory, name, definition) && isDefaultCandidate(definition));
275+
}
276+
277+
private boolean isAutowireCandidate(ConfigurableListableBeanFactory beanFactory, String name,
278+
BeanDefinition definition) {
279+
return definition.isAutowireCandidate() || isScopeTargetAutowireCandidate(beanFactory, name);
280+
}
281+
282+
private boolean isScopeTargetAutowireCandidate(ConfigurableListableBeanFactory beanFactory, String name) {
283+
try {
284+
return ScopedProxyUtils.isScopedTarget(name)
285+
&& beanFactory.getBeanDefinition(ScopedProxyUtils.getOriginalBeanName(name)).isAutowireCandidate();
286+
}
287+
catch (NoSuchBeanDefinitionException ex) {
288+
return false;
289+
}
269290
}
270291

271292
private boolean isDefaultCandidate(BeanDefinition definition) {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
import org.springframework.context.annotation.Import;
4646
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
4747
import org.springframework.context.annotation.ImportResource;
48+
import org.springframework.context.annotation.Scope;
49+
import org.springframework.context.annotation.ScopedProxyMode;
50+
import org.springframework.context.support.SimpleThreadScope;
4851
import org.springframework.core.type.AnnotationMetadata;
4952
import org.springframework.util.Assert;
5053
import org.springframework.util.StringUtils;
@@ -124,6 +127,16 @@ void testAnnotationOnMissingBeanCondition() {
124127
});
125128
}
126129

130+
@Test
131+
void testAnnotationOnMissingBeanConditionWithScopedProxy() {
132+
this.contextRunner.withInitializer(this::registerScope)
133+
.withUserConfiguration(ScopedExampleBeanConfiguration.class, OnAnnotationConfiguration.class)
134+
.run((context) -> {
135+
assertThat(context).doesNotHaveBean("bar");
136+
assertThat(context.getBean(ScopedExampleBean.class)).hasToString("test");
137+
});
138+
}
139+
127140
@Test
128141
void testAnnotationOnMissingBeanConditionWithEagerFactoryBean() {
129142
// Rigorous test for SPR-11069
@@ -407,6 +420,10 @@ private Consumer<ConfigurableApplicationContext> exampleBeanRequirement(String..
407420
};
408421
}
409422

423+
private void registerScope(ConfigurableApplicationContext applicationContext) {
424+
applicationContext.getBeanFactory().registerScope("test", new TestScope());
425+
}
426+
410427
@Configuration(proxyBeanMethods = false)
411428
static class OnBeanInAncestorsConfiguration {
412429

@@ -665,6 +682,17 @@ String foo() {
665682

666683
}
667684

685+
@Configuration(proxyBeanMethods = false)
686+
@TestAnnotation
687+
static class ScopedFooConfiguration {
688+
689+
@Bean
690+
String foo() {
691+
return "foo";
692+
}
693+
694+
}
695+
668696
@Configuration(proxyBeanMethods = false)
669697
static class NotAutowireCandidateConfig {
670698

@@ -717,6 +745,12 @@ ExampleBean exampleBean() {
717745

718746
}
719747

748+
@Configuration(proxyBeanMethods = false)
749+
@Import(ScopedExampleBean.class)
750+
static class ScopedExampleBeanConfiguration {
751+
752+
}
753+
720754
@Configuration(proxyBeanMethods = false)
721755
static class UnrelatedExampleBeanConfiguration {
722756

@@ -893,6 +927,15 @@ public String toString() {
893927

894928
}
895929

930+
@Scope(scopeName = "test", proxyMode = ScopedProxyMode.TARGET_CLASS)
931+
static class ScopedExampleBean extends ExampleBean {
932+
933+
ScopedExampleBean() {
934+
super("test");
935+
}
936+
937+
}
938+
896939
static class CustomExampleBean extends ExampleBean {
897940

898941
CustomExampleBean() {
@@ -931,4 +974,8 @@ public String toString() {
931974

932975
}
933976

977+
static class TestScope extends SimpleThreadScope {
978+
979+
}
980+
934981
}

0 commit comments

Comments
 (0)