Skip to content

Commit 3fff3b8

Browse files
committed
Merge branch '6.2.x'
# Conflicts: # spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java # spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
2 parents bd9ae6b + ace2f0a commit 3fff3b8

File tree

7 files changed

+120
-71
lines changed

7 files changed

+120
-71
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,10 +40,14 @@
4040
* (which the methods defined on the ListableBeanFactory interface don't,
4141
* in contrast to the methods defined on the BeanFactory interface).
4242
*
43+
* <p><b>NOTE:</b> It is generally preferable to use {@link ObjectProvider#stream()}
44+
* via {@link BeanFactory#getBeanProvider} instead of this utility class.
45+
*
4346
* @author Rod Johnson
4447
* @author Juergen Hoeller
4548
* @author Chris Beams
4649
* @since 04.07.2003
50+
* @see BeanFactory#getBeanProvider
4751
*/
4852
public abstract class BeanFactoryUtils {
4953

@@ -309,7 +313,7 @@ public static String[] beanNamesForAnnotationIncludingAncestors(
309313
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
310314
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
311315
* @param lbf the bean factory
312-
* @param type type of bean to match
316+
* @param type the type of bean to match
313317
* @return the Map of matching bean instances, or an empty Map if none
314318
* @throws BeansException if a bean could not be created
315319
* @see ListableBeanFactory#getBeansOfType(Class)
@@ -348,7 +352,7 @@ public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFacto
348352
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
349353
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
350354
* @param lbf the bean factory
351-
* @param type type of bean to match
355+
* @param type the type of bean to match
352356
* @param includeNonSingletons whether to include prototype or scoped beans too
353357
* or just singletons (also applies to FactoryBeans)
354358
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
@@ -396,7 +400,7 @@ public static <T> Map<String, T> beansOfTypeIncludingAncestors(
396400
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
397401
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
398402
* @param lbf the bean factory
399-
* @param type type of bean to match
403+
* @param type the type of bean to match
400404
* @return the matching bean instance
401405
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
402406
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
@@ -426,7 +430,7 @@ public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<
426430
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
427431
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
428432
* @param lbf the bean factory
429-
* @param type type of bean to match
433+
* @param type the type of bean to match
430434
* @param includeNonSingletons whether to include prototype or scoped beans too
431435
* or just singletons (also applies to FactoryBeans)
432436
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
@@ -458,7 +462,7 @@ public static <T> T beanOfTypeIncludingAncestors(
458462
* <p>This version of {@code beanOfType} automatically includes
459463
* prototypes and FactoryBeans.
460464
* @param lbf the bean factory
461-
* @param type type of bean to match
465+
* @param type the type of bean to match
462466
* @return the matching bean instance
463467
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
464468
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
@@ -482,7 +486,7 @@ public static <T> T beanOfType(ListableBeanFactory lbf, Class<T> type) throws Be
482486
* only raw FactoryBeans will be checked (which doesn't require initialization
483487
* of each FactoryBean).
484488
* @param lbf the bean factory
485-
* @param type type of bean to match
489+
* @param type the type of bean to match
486490
* @param includeNonSingletons whether to include prototype or scoped beans too
487491
* or just singletons (also applies to FactoryBeans)
488492
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
@@ -530,7 +534,7 @@ private static String[] mergeNamesWithParent(String[] result, String[] parentRes
530534

531535
/**
532536
* Extract a unique bean for the given type from the given Map of matching beans.
533-
* @param type type of bean to match
537+
* @param type the type of bean to match
534538
* @param matchingBeans all matching beans found
535539
* @return the unique bean instance
536540
* @throws NoSuchBeanDefinitionException if no bean of the given type was found

spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@
5656
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
5757

5858
/**
59-
* A predicate for unfiltered type matches.
59+
* A predicate for unfiltered type matches, including non-default candidates
60+
* but still excluding non-autowire candidates when used on injection points.
6061
* @since 6.2.3
6162
* @see #stream(Predicate)
6263
* @see #orderedStream(Predicate)
64+
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
65+
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#isDefaultCandidate()
6366
*/
6467
Predicate<Class<?>> UNFILTERED = (clazz -> true);
6568

@@ -209,7 +212,7 @@ default Iterator<T> iterator() {
209212
* without specific ordering guarantees (but typically in registration order).
210213
* <p>Note: The result may be filtered by default according to qualifiers on the
211214
* injection point versus target beans and the general autowire candidate status
212-
* of matching beans. For custom filtering against the raw type matches, use
215+
* of matching beans. For custom filtering against type-matching candidates, use
213216
* {@link #stream(Predicate)} instead (potentially with {@link #UNFILTERED}).
214217
* @since 5.1
215218
* @see #iterator()
@@ -234,7 +237,7 @@ default Stream<T> stream() {
234237
* if necessary.
235238
* <p>Note: The result may be filtered by default according to qualifiers on the
236239
* injection point versus target beans and the general autowire candidate status
237-
* of matching beans. For custom filtering against the raw type matches, use
240+
* of matching beans. For custom filtering against type-matching candidates, use
238241
* {@link #stream(Predicate)} instead (potentially with {@link #UNFILTERED}).
239242
* @since 5.1
240243
* @see #stream()

spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java

+20
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import org.jspecify.annotations.Nullable;
3636

3737
import org.springframework.beans.BeanMetadataElement;
38+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3839
import org.springframework.beans.factory.ObjectFactory;
40+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
3941
import org.springframework.beans.factory.config.TypedStringValue;
4042
import org.springframework.util.Assert;
4143
import org.springframework.util.ClassUtils;
@@ -260,6 +262,24 @@ else if (arg instanceof TypedStringValue typedValue) {
260262
return method.getReturnType();
261263
}
262264

265+
/**
266+
* Check the autowire-candidate status for the specified bean.
267+
* @param beanFactory the bean factory
268+
* @param beanName the name of the bean to check
269+
* @return whether the specified bean qualifies as an autowire candidate
270+
* @since 6.2.3
271+
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
272+
*/
273+
public static boolean isAutowireCandidate(ConfigurableBeanFactory beanFactory, String beanName) {
274+
try {
275+
return beanFactory.getMergedBeanDefinition(beanName).isAutowireCandidate();
276+
}
277+
catch (NoSuchBeanDefinitionException ex) {
278+
// A manually registered singleton instance not backed by a BeanDefinition.
279+
return true;
280+
}
281+
}
282+
263283

264284
/**
265285
* Reflective {@link InvocationHandler} for lazy access to the current target object.

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -2480,6 +2480,7 @@ private Stream<Object> resolveStream(boolean ordered) {
24802480
@Override
24812481
public Stream<Object> stream(Predicate<Class<?>> customFilter) {
24822482
return Arrays.stream(getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true))
2483+
.filter(name -> AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, name))
24832484
.filter(name -> customFilter.test(getType(name)))
24842485
.map(name -> getBean(name))
24852486
.filter(bean -> !(bean instanceof NullBean));
@@ -2493,7 +2494,8 @@ public Stream<Object> orderedStream(Predicate<Class<?>> customFilter) {
24932494
}
24942495
Map<String, Object> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
24952496
for (String beanName : beanNames) {
2496-
if (customFilter.test(getType(beanName))) {
2497+
if (AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, beanName) &&
2498+
customFilter.test(getType(beanName))) {
24972499
Object beanInstance = getBean(beanName);
24982500
if (!(beanInstance instanceof NullBean)) {
24992501
matchingBeans.put(beanName, beanInstance);
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,13 @@
1616

1717
package org.springframework.beans.factory.support;
1818

19-
import org.jspecify.annotations.Nullable;
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
2021

21-
import org.springframework.beans.factory.config.BeanDefinitionHolder;
22-
import org.springframework.beans.factory.config.DependencyDescriptor;
22+
import org.springframework.beans.BeansException;
23+
import org.springframework.beans.factory.BeanFactoryUtils;
24+
import org.springframework.beans.factory.ListableBeanFactory;
25+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2326

2427
/**
2528
* {@link AutowireCandidateResolver} implementation to use when no annotation
@@ -37,42 +40,6 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
3740
*/
3841
public static final SimpleAutowireCandidateResolver INSTANCE = new SimpleAutowireCandidateResolver();
3942

40-
41-
@Override
42-
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
43-
return bdHolder.getBeanDefinition().isAutowireCandidate();
44-
}
45-
46-
@Override
47-
public boolean isRequired(DependencyDescriptor descriptor) {
48-
return descriptor.isRequired();
49-
}
50-
51-
@Override
52-
public boolean hasQualifier(DependencyDescriptor descriptor) {
53-
return false;
54-
}
55-
56-
@Override
57-
public @Nullable String getSuggestedName(DependencyDescriptor descriptor) {
58-
return null;
59-
}
60-
61-
@Override
62-
public @Nullable Object getSuggestedValue(DependencyDescriptor descriptor) {
63-
return null;
64-
}
65-
66-
@Override
67-
public @Nullable Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
68-
return null;
69-
}
70-
71-
@Override
72-
public @Nullable Class<?> getLazyResolutionProxyClass(DependencyDescriptor descriptor, @Nullable String beanName) {
73-
return null;
74-
}
75-
7643
/**
7744
* This implementation returns {@code this} as-is.
7845
* @see #INSTANCE
@@ -82,4 +49,31 @@ public AutowireCandidateResolver cloneIfNecessary() {
8249
return this;
8350
}
8451

52+
53+
/**
54+
* Resolve a map of all beans of the given type, also picking up beans defined in
55+
* ancestor bean factories, with the specific condition that each bean actually
56+
* has autowire candidate status. This matches simple injection point resolution
57+
* as implemented by this {@link AutowireCandidateResolver} strategy, including
58+
* beans which are not marked as default candidates but excluding beans which
59+
* are not even marked as autowire candidates.
60+
* @param lbf the bean factory
61+
* @param type the type of bean to match
62+
* @return the Map of matching bean instances, or an empty Map if none
63+
* @throws BeansException if a bean could not be created
64+
* @since 6.2.3
65+
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
66+
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
67+
* @see AbstractBeanDefinition#isDefaultCandidate()
68+
*/
69+
public static <T> Map<String, T> resolveAutowireCandidates(ConfigurableListableBeanFactory lbf, Class<T> type) {
70+
Map<String, T> candidates = new LinkedHashMap<>();
71+
for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(lbf, type)) {
72+
if (AutowireUtils.isAutowireCandidate(lbf, beanName)) {
73+
candidates.put(beanName, lbf.getBean(beanName, type));
74+
}
75+
}
76+
return candidates;
77+
}
78+
8579
}

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

+16-8
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
import org.springframework.beans.factory.BeanCreationException;
5151
import org.springframework.beans.factory.BeanFactory;
52+
import org.springframework.beans.factory.BeanFactoryUtils;
5253
import org.springframework.beans.factory.BeanNameAware;
5354
import org.springframework.beans.factory.DisposableBean;
5455
import org.springframework.beans.factory.FactoryBean;
@@ -65,6 +66,7 @@
6566
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
6667
import org.springframework.beans.factory.support.GenericBeanDefinition;
6768
import org.springframework.beans.factory.support.RootBeanDefinition;
69+
import org.springframework.beans.factory.support.SimpleAutowireCandidateResolver;
6870
import org.springframework.beans.testfixture.beans.DerivedTestBean;
6971
import org.springframework.beans.testfixture.beans.ITestBean;
7072
import org.springframework.beans.testfixture.beans.IndexedTestBean;
@@ -1756,14 +1758,17 @@ void objectProviderInjectionWithNonCandidatesInStream() {
17561758
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
17571759
tb2.setFactoryMethodName("newTestBean2");
17581760
bf.registerBeanDefinition("testBean2", tb2);
1761+
1762+
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
17591763
RootBeanDefinition tb3 = new RootBeanDefinition(TestBean.class);
17601764
tb3.setAutowireCandidate(false);
17611765
tb3.setLazyInit(true);
1762-
bf.registerBeanDefinition("testBean3", tb3);
1766+
parent.registerBeanDefinition("testBean3", tb3);
17631767
RootBeanDefinition tb4 = new RootBeanDefinition(DerivedTestBean.class);
17641768
tb4.setDefaultCandidate(false);
17651769
tb4.setLazyInit(true);
1766-
bf.registerBeanDefinition("testBean4", tb4);
1770+
parent.registerBeanDefinition("testBean4", tb4);
1771+
bf.setParentBeanFactory(parent);
17671772

17681773
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
17691774
assertThat(bean.streamTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
@@ -1772,16 +1777,19 @@ void objectProviderInjectionWithNonCandidatesInStream() {
17721777
bf.getBean("testBean1", TestBean.class));
17731778
assertThat(bf.containsSingleton("testBean3")).isFalse();
17741779
assertThat(bean.plainTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
1775-
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean3", TestBean.class));
1780+
bf.getBean("testBean2", TestBean.class));
17761781
assertThat(bean.plainTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
1777-
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean3", TestBean.class));
1782+
bf.getBean("testBean1", TestBean.class));
17781783
assertThat(bf.containsSingleton("testBean4")).isFalse();
17791784
assertThat(bean.allTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
1780-
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean3", TestBean.class),
1781-
bf.getBean("testBean4", TestBean.class));
1785+
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean4", TestBean.class));
17821786
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
1783-
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean3", TestBean.class),
1784-
bf.getBean("testBean4", TestBean.class));
1787+
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean4", TestBean.class));
1788+
1789+
Map<String, TestBean> typeMatches = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, TestBean.class);
1790+
assertThat(typeMatches.remove("testBean3")).isNotNull();
1791+
Map<String, TestBean> candidates = SimpleAutowireCandidateResolver.resolveAutowireCandidates(bf, TestBean.class);
1792+
assertThat(candidates).containsExactlyEntriesOf(candidates);
17851793
}
17861794

17871795
@Test

0 commit comments

Comments
 (0)