Consistent type-based bean lookup for internal resolution paths

Includes additional tests for List/ObjectProvider dependencies.

See gh-35101
This commit is contained in:
Juergen Hoeller 2025-06-26 12:51:55 +02:00
parent 2e9e45ee55
commit 06ef82e9a5
3 changed files with 107 additions and 22 deletions

View File

@ -496,7 +496,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
public Stream<T> stream() {
return Arrays.stream(beanNamesForStream(requiredType, true, allowEagerInit))
.map(name -> (T) getBean(name))
.map(name -> (T) resolveBean(name, requiredType))
.filter(bean -> !(bean instanceof NullBean));
}
@SuppressWarnings("unchecked")
@ -508,7 +508,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
Map<String, T> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
for (String beanName : beanNames) {
Object beanInstance = getBean(beanName);
Object beanInstance = resolveBean(beanName, requiredType);
if (!(beanInstance instanceof NullBean)) {
matchingBeans.put(beanName, (T) beanInstance);
}
@ -521,7 +521,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
public Stream<T> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
return Arrays.stream(beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit))
.filter(name -> customFilter.test(getType(name)))
.map(name -> (T) getBean(name))
.map(name -> (T) resolveBean(name, requiredType))
.filter(bean -> !(bean instanceof NullBean));
}
@SuppressWarnings("unchecked")
@ -534,7 +534,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Map<String, T> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
for (String beanName : beanNames) {
if (customFilter.test(getType(beanName))) {
Object beanInstance = getBean(beanName);
Object beanInstance = resolveBean(beanName, requiredType);
if (!(beanInstance instanceof NullBean)) {
matchingBeans.put(beanName, (T) beanInstance);
}
@ -1207,6 +1207,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
private Object resolveBean(String beanName, ResolvableType requiredType) {
try {
// Need to provide required type for SmartFactoryBean
return getBean(beanName, requiredType.toClass());
}
catch (BeanNotOfRequiredTypeException ex) {
// Probably a null bean...
return getBean(beanName);
}
}
private static String getThreadNamePrefix() {
String name = Thread.currentThread().getName();
int numberSeparator = name.lastIndexOf('-');
@ -1542,7 +1553,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Map<String, Object> candidates = CollectionUtils.newLinkedHashMap(candidateNames.length);
for (String beanName : candidateNames) {
if (containsSingleton(beanName) && args == null) {
Object beanInstance = getBean(beanName);
Object beanInstance = resolveBean(beanName, requiredType);
candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
}
else {
@ -1659,7 +1670,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (autowiredBeanNames != null) {
autowiredBeanNames.add(dependencyName);
}
Object dependencyBean = getBean(dependencyName);
Object dependencyBean = resolveBean(dependencyName, descriptor.getResolvableType());
return resolveInstance(dependencyBean, descriptor, type, dependencyName);
}
}
@ -2582,16 +2593,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
public Stream<Object> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
return Arrays.stream(beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true))
ResolvableType type = this.descriptor.getResolvableType();
return Arrays.stream(beanNamesForStream(type, includeNonSingletons, true))
.filter(name -> AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, name))
.filter(name -> customFilter.test(getType(name)))
.map(name -> getBean(name))
.map(name -> resolveBean(name, type))
.filter(bean -> !(bean instanceof NullBean));
}
@Override
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
String[] beanNames = beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true);
ResolvableType type = this.descriptor.getResolvableType();
String[] beanNames = beanNamesForStream(type, includeNonSingletons, true);
if (beanNames.length == 0) {
return Stream.empty();
}
@ -2599,7 +2612,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
for (String beanName : beanNames) {
if (AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, beanName) &&
customFilter.test(getType(beanName))) {
Object beanInstance = getBean(beanName);
Object beanInstance = resolveBean(beanName, type);
if (!(beanInstance instanceof NullBean)) {
matchingBeans.put(beanName, beanInstance);
}

View File

@ -307,7 +307,7 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
public T getObject() throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0], requiredType);
return (T) getBean(beanNames[0], requiredType.toClass());
}
else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
@ -333,7 +333,7 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
public @Nullable T getIfAvailable() throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0]);
return (T) getBean(beanNames[0], requiredType.toClass());
}
else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
@ -346,7 +346,7 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
public @Nullable T getIfUnique() throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0]);
return (T) getBean(beanNames[0], requiredType.toClass());
}
else {
return null;
@ -354,7 +354,8 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
}
@Override
public Stream<T> stream() {
return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name));
return Arrays.stream(getBeanNamesForType(requiredType))
.map(name -> (T) getBean(name, requiredType.toClass()));
}
};
}

View File

@ -464,8 +464,40 @@ class BeanFactoryUtilsTests {
lbf.registerSingleton("fb2", fb2);
lbf.registerSingleton("sfb1", sfb1);
lbf.registerSingleton("sfb2", sfb2);
lbf.registerBeanDefinition("recipient",
new RootBeanDefinition(Recipient.class, RootBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));
testSupportsMultipleTypesWithStaticFactory(lbf);
Recipient recipient = lbf.getBean("recipient", Recipient.class);
assertThat(recipient.sfb1).isSameAs(lbf.getBean("sfb1", TestBean.class));
assertThat(recipient.sfb2).isSameAs(lbf.getBean("sfb2", TestBean.class));
List<ITestBean> testBeanList = recipient.testBeanList;
assertThat(testBeanList).hasSize(5);
assertThat(testBeanList.get(0)).isSameAs(bean);
assertThat(testBeanList.get(1)).isSameAs(fb1.getObject());
assertThat(testBeanList.get(2)).isInstanceOf(TestBean.class);
assertThat(testBeanList.get(3)).isSameAs(lbf.getBean("sfb1", TestBean.class));
assertThat(testBeanList.get(4)).isSameAs(lbf.getBean("sfb2", TestBean.class));
List<CharSequence> stringList = recipient.stringList;
assertThat(stringList).hasSize(2);
assertThat(stringList.get(0)).isSameAs(lbf.getBean("sfb1", String.class));
assertThat(stringList.get(1)).isSameAs(lbf.getBean("sfb2", String.class));
testBeanList = recipient.testBeanProvider.stream().toList();
assertThat(testBeanList).hasSize(5);
assertThat(testBeanList.get(0)).isSameAs(bean);
assertThat(testBeanList.get(1)).isSameAs(fb1.getObject());
assertThat(testBeanList.get(2)).isInstanceOf(TestBean.class);
assertThat(testBeanList.get(3)).isSameAs(lbf.getBean("sfb1", TestBean.class));
assertThat(testBeanList.get(4)).isSameAs(lbf.getBean("sfb2", TestBean.class));
stringList = recipient.stringProvider.stream().toList();
assertThat(stringList).hasSize(2);
assertThat(stringList.get(0)).isSameAs(lbf.getBean("sfb1", String.class));
assertThat(stringList.get(1)).isSameAs(lbf.getBean("sfb2", String.class));
testSupportsMultipleTypes(lbf);
}
@Test
@ -483,22 +515,35 @@ class BeanFactoryUtilsTests {
lbf.addBean("sfb1", sfb1);
lbf.addBean("sfb2", sfb2);
testSupportsMultipleTypesWithStaticFactory(lbf);
testSupportsMultipleTypes(lbf);
}
void testSupportsMultipleTypesWithStaticFactory(ListableBeanFactory lbf) {
void testSupportsMultipleTypes(ListableBeanFactory lbf) {
List<ITestBean> testBeanList = lbf.getBeanProvider(ITestBean.class).stream().toList();
assertThat(testBeanList).hasSize(5);
assertThat(testBeanList.get(0)).isSameAs(lbf.getBean("bean", TestBean.class));
assertThat(testBeanList.get(1)).isSameAs(lbf.getBean("fb1", TestBean.class));
assertThat(testBeanList.get(2)).isInstanceOf(TestBean.class);
assertThat(testBeanList.get(3)).isSameAs(lbf.getBean("sfb1", TestBean.class));
assertThat(testBeanList.get(4)).isSameAs(lbf.getBean("sfb2", TestBean.class));
List<CharSequence> stringList = lbf.getBeanProvider(CharSequence.class).stream().toList();
assertThat(stringList).hasSize(2);
assertThat(stringList.get(0)).isSameAs(lbf.getBean("sfb1", String.class));
assertThat(stringList.get(1)).isSameAs(lbf.getBean("sfb2", String.class));
Map<String, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, ITestBean.class);
assertThat(beans).hasSize(5);
assertThat(beans.get("bean")).isSameAs(lbf.getBean("bean"));
assertThat(beans.get("fb1")).isSameAs(lbf.getBean("&fb1", DummyFactory.class).getObject());
assertThat(beans.get("fb1")).isSameAs(lbf.getBean("fb1",TestBean.class));
assertThat(beans.get("fb2")).isInstanceOf(TestBean.class);
assertThat(beans.get("sfb1")).isInstanceOf(TestBean.class);
assertThat(beans.get("sfb2")).isInstanceOf(TestBean.class);
assertThat(beans.get("sfb1")).isSameAs(lbf.getBean("sfb1", TestBean.class));
assertThat(beans.get("sfb2")).isSameAs(lbf.getBean("sfb2", TestBean.class));
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, CharSequence.class);
assertThat(beans).hasSize(2);
assertThat(beans.get("sfb1")).isInstanceOf(String.class);
assertThat(beans.get("sfb2")).isInstanceOf(String.class);
assertThat(beans.get("sfb1")).isSameAs(lbf.getBean("sfb1", String.class));
assertThat(beans.get("sfb2")).isSameAs(lbf.getBean("sfb1", String.class));
assertThat(lbf.getBean("sfb1", ITestBean.class)).isInstanceOf(TestBean.class);
assertThat(lbf.getBean("sfb2", ITestBean.class)).isInstanceOf(TestBean.class);
@ -604,4 +649,30 @@ class BeanFactoryUtilsTests {
}
}
static class Recipient {
public Recipient(ITestBean sfb1, ITestBean sfb2, List<ITestBean> testBeanList, List<CharSequence> stringList,
ObjectProvider<ITestBean> testBeanProvider, ObjectProvider<CharSequence> stringProvider) {
this.sfb1 = sfb1;
this.sfb2 = sfb2;
this.testBeanList = testBeanList;
this.stringList = stringList;
this.testBeanProvider = testBeanProvider;
this.stringProvider = stringProvider;
}
ITestBean sfb1;
ITestBean sfb2;
List<ITestBean> testBeanList;
List<CharSequence> stringList;
ObjectProvider<ITestBean> testBeanProvider;
ObjectProvider<CharSequence> stringProvider;
}
}