diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java index d62c3ee6d3c..06769790d10 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java @@ -274,7 +274,7 @@ public interface ObjectProvider extends ObjectFactory, Iterable { * @see #orderedStream(Predicate) */ default Stream stream(Predicate> customFilter) { - return stream().filter(obj -> customFilter.test(obj.getClass())); + return stream(customFilter, true); } /** @@ -288,6 +288,44 @@ public interface ObjectProvider extends ObjectFactory, Iterable { * @see #stream(Predicate) */ default Stream orderedStream(Predicate> customFilter) { + return orderedStream(customFilter, true); + } + + /** + * Return a custom-filtered {@link Stream} over all matching object instances, + * without specific ordering guarantees (but typically in registration order). + * @param customFilter a custom type filter for selecting beans among the raw + * bean type matches (or {@link #UNFILTERED} for all raw type matches without + * any default filtering) + * @param includeNonSingletons whether to include prototype or scoped beans too + * or just singletons (also applies to FactoryBeans) + * @since 6.2.5 + * @see #stream(Predicate) + * @see #orderedStream(Predicate, boolean) + */ + default Stream stream(Predicate> customFilter, boolean includeNonSingletons) { + if (!includeNonSingletons) { + throw new UnsupportedOperationException("Only supports includeNonSingletons=true by default"); + } + return stream().filter(obj -> customFilter.test(obj.getClass())); + } + + /** + * Return a custom-filtered {@link Stream} over all matching object instances, + * pre-ordered according to the factory's common order comparator. + * @param customFilter a custom type filter for selecting beans among the raw + * bean type matches (or {@link #UNFILTERED} for all raw type matches without + * any default filtering) + * @param includeNonSingletons whether to include prototype or scoped beans too + * or just singletons (also applies to FactoryBeans) + * @since 6.2.5 + * @see #orderedStream() + * @see #stream(Predicate) + */ + default Stream orderedStream(Predicate> customFilter, boolean includeNonSingletons) { + if (!includeNonSingletons) { + throw new UnsupportedOperationException("Only supports includeNonSingletons=true by default"); + } return orderedStream().filter(obj -> customFilter.test(obj.getClass())); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 57c1d1a38fa..9e879a96a5f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -487,14 +487,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @SuppressWarnings("unchecked") @Override public Stream stream() { - return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit)) + return Arrays.stream(beanNamesForStream(requiredType, true, allowEagerInit)) .map(name -> (T) getBean(name)) .filter(bean -> !(bean instanceof NullBean)); } @SuppressWarnings("unchecked") @Override public Stream orderedStream() { - String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit); + String[] beanNames = beanNamesForStream(requiredType, true, allowEagerInit); if (beanNames.length == 0) { return Stream.empty(); } @@ -510,16 +510,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } @SuppressWarnings("unchecked") @Override - public Stream stream(Predicate> customFilter) { - return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit)) + public Stream stream(Predicate> customFilter, boolean includeNonSingletons) { + return Arrays.stream(beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit)) .filter(name -> customFilter.test(getType(name))) .map(name -> (T) getBean(name)) .filter(bean -> !(bean instanceof NullBean)); } @SuppressWarnings("unchecked") @Override - public Stream orderedStream(Predicate> customFilter) { - String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit); + public Stream orderedStream(Predicate> customFilter, boolean includeNonSingletons) { + String[] beanNames = beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit); if (beanNames.length == 0) { return Stream.empty(); } @@ -559,8 +559,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return null; } - private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) { - return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit); + private String[] beanNamesForStream(ResolvableType requiredType, boolean includeNonSingletons, boolean allowEagerInit) { + return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, includeNonSingletons, allowEagerInit); } @Override @@ -2544,8 +2544,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } @Override - public Stream stream(Predicate> customFilter) { - return Arrays.stream(getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true)) + public Stream stream(Predicate> customFilter, boolean includeNonSingletons) { + return Arrays.stream(beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true)) .filter(name -> AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, name)) .filter(name -> customFilter.test(getType(name))) .map(name -> getBean(name)) @@ -2553,8 +2553,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } @Override - public Stream orderedStream(Predicate> customFilter) { - String[] beanNames = getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true); + public Stream orderedStream(Predicate> customFilter, boolean includeNonSingletons) { + String[] beanNames = beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true); if (beanNames.length == 0) { return Stream.empty(); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index 4238da225ea..3216b92938d 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1519,34 +1519,34 @@ class DefaultListableBeanFactoryTests { bd2.setBeanClass(DerivedTestBean.class); bd2.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "highest")))); bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE); + bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("bean2", bd2); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName)) .containsExactly("highest", "lowest"); - assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName)) - .containsExactly("highest", "lowest"); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz)) .map(TestBean::getName)).containsExactly("lowest"); + assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName)) + .containsExactly("highest", "lowest"); + assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName)) + .containsExactly("lowest"); } @Test - void orderFromAttributeOverrideAnnotation() { + void orderFromAttributeOverridesAnnotation() { lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); RootBeanDefinition rbd1 = new RootBeanDefinition(LowestPrecedenceTestBeanFactoryBean.class); rbd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE); lbf.registerBeanDefinition("lowestPrecedenceFactory", rbd1); RootBeanDefinition rbd2 = new RootBeanDefinition(HighestPrecedenceTestBeanFactoryBean.class); rbd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE); + rbd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("highestPrecedenceFactory", rbd2); - GenericBeanDefinition bd1 = new GenericBeanDefinition(); - bd1.setFactoryBeanName("highestPrecedenceFactory"); - lbf.registerBeanDefinition("bean1", bd1); - GenericBeanDefinition bd2 = new GenericBeanDefinition(); - bd2.setFactoryBeanName("lowestPrecedenceFactory"); - lbf.registerBeanDefinition("bean2", bd2); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName)) .containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean"); assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName)) .containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean"); + assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName)) + .containsExactly("fromLowestPrecedenceTestBeanFactoryBean"); } @Test @@ -1987,7 +1987,6 @@ class DefaultListableBeanFactoryTests { void getBeanByTypeInstanceWithAmbiguity() { RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99); RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependency.class); - bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd2.getConstructorArgumentValues().addGenericArgumentValue("43"); lbf.registerBeanDefinition("bd1", bd1); lbf.registerBeanDefinition("bd2", bd2); @@ -2028,6 +2027,10 @@ class DefaultListableBeanFactoryTests { assertThat(resolved).hasSize(2); assertThat(resolved).contains(lbf.getBean("bd1")); assertThat(resolved).contains(lbf.getBean("bd2")); + + resolved = provider.stream(ObjectProvider.UNFILTERED, false).collect(Collectors.toSet()); + assertThat(resolved).hasSize(1); + assertThat(resolved).contains(lbf.getBean("bd2")); } @Test @@ -2082,6 +2085,9 @@ class DefaultListableBeanFactoryTests { assertThat(resolved).hasSize(2); assertThat(resolved).contains(lbf.getBean("bd1")); assertThat(resolved).contains(lbf.getBean("bd2")); + + resolved = provider.stream(ObjectProvider.UNFILTERED, false).collect(Collectors.toSet()); + assertThat(resolved).isEmpty(); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index f53f9ff5a48..82917831f99 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -1614,6 +1614,10 @@ class AutowiredAnnotationBeanPostProcessorTests { assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class)); testBeans = bean.allTestBeansInOrder(); assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class)); + testBeans = bean.allSingletonBeans(); + assertThat(testBeans).isEmpty(); + testBeans = bean.allSingletonBeansInOrder(); + assertThat(testBeans).isEmpty(); } @Test @@ -1648,6 +1652,12 @@ class AutowiredAnnotationBeanPostProcessorTests { testBeans = bean.allTestBeansInOrder(); assertThat(testBeans).hasSize(1); assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class)); + testBeans = bean.allSingletonBeans(); + assertThat(testBeans).hasSize(1); + assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class)); + testBeans = bean.allSingletonBeansInOrder(); + assertThat(testBeans).hasSize(1); + assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class)); } @Test @@ -1675,6 +1685,10 @@ class AutowiredAnnotationBeanPostProcessorTests { assertThat(testBeans).isEmpty(); testBeans = bean.allTestBeansInOrder(); assertThat(testBeans).isEmpty(); + testBeans = bean.allSingletonBeans(); + assertThat(testBeans).isEmpty(); + testBeans = bean.allSingletonBeansInOrder(); + assertThat(testBeans).isEmpty(); } @Test @@ -1698,6 +1712,8 @@ class AutowiredAnnotationBeanPostProcessorTests { assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean1, testBean2); assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2); assertThat(bean.allTestBeansInOrder()).containsExactly(testBean1, testBean2); + assertThat(bean.allSingletonBeans()).containsExactly(testBean1, testBean2); + assertThat(bean.allSingletonBeansInOrder()).containsExactly(testBean1, testBean2); } @Test @@ -1728,6 +1744,8 @@ class AutowiredAnnotationBeanPostProcessorTests { assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean2, testBean1); assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2); assertThat(bean.allTestBeansInOrder()).containsExactly(testBean2, testBean1); + assertThat(bean.allSingletonBeans()).containsExactly(testBean1, testBean2); + assertThat(bean.allSingletonBeansInOrder()).containsExactly(testBean2, testBean1); } @Test @@ -1739,7 +1757,7 @@ class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("testBean1", tb1); RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class); tb2.setFactoryMethodName("newTestBean2"); - tb2.setLazyInit(true); + tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("testBean2", tb2); ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class); @@ -1747,6 +1765,7 @@ class AutowiredAnnotationBeanPostProcessorTests { bf.getBean("testBean1", TestBean.class)); assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class), bf.getBean("testBean1", TestBean.class)); + assertThat(bean.allSingletonBeansInOrder()).containsExactly(bf.getBean("testBean1", TestBean.class)); } @Test @@ -1757,6 +1776,7 @@ class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("testBean1", tb1); RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class); tb2.setFactoryMethodName("newTestBean2"); + tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("testBean2", tb2); DefaultListableBeanFactory parent = new DefaultListableBeanFactory(); @@ -1789,6 +1809,10 @@ class AutowiredAnnotationBeanPostProcessorTests { bf.getBean("testBean2", TestBean.class), bf.getBean("testBean4", TestBean.class)); assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class), bf.getBean("testBean1", TestBean.class), bf.getBean("testBean4", TestBean.class)); + assertThat(bean.allSingletonBeans()).containsExactly(bf.getBean("testBean1", TestBean.class), + bf.getBean("testBean4", TestBean.class)); + assertThat(bean.allSingletonBeansInOrder()).containsExactly(bf.getBean("testBean1", TestBean.class), + bf.getBean("testBean4", TestBean.class)); Map typeMatches = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, TestBean.class); assertThat(typeMatches.remove("testBean3")).isNotNull(); @@ -2370,7 +2394,7 @@ class AutowiredAnnotationBeanPostProcessorTests { } @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings("unchecked") void genericsBasedConstructorInjectionWithNonTypedTarget() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); @@ -3393,6 +3417,14 @@ class AutowiredAnnotationBeanPostProcessorTests { public List allTestBeansInOrder() { return this.testBean.orderedStream(ObjectProvider.UNFILTERED).toList(); } + + public List allSingletonBeans() { + return this.testBean.stream(ObjectProvider.UNFILTERED, false).toList(); + } + + public List allSingletonBeansInOrder() { + return this.testBean.orderedStream(ObjectProvider.UNFILTERED, false).toList(); + } }