diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java index 4df284c308..389f19c9e4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java @@ -87,6 +87,43 @@ public interface ListableBeanFactory extends BeanFactory { */ String[] getBeanDefinitionNames(); + /** + * Return a provider for the specified bean, allowing for lazy on-demand retrieval + * of instances, including availability and uniqueness options. + * @param requiredType type the bean must match; can be an interface or superclass + * @param allowEagerInit whether stream-based access may initialize lazy-init + * singletons and objects created by FactoryBeans (or by factory methods + * with a "factory-bean" reference) for the type check + * @return a corresponding provider handle + * @since 5.3 + * @see #getBeanProvider(ResolvableType, boolean) + * @see #getBeanProvider(Class) + * @see #getBeansOfType(Class, boolean, boolean) + * @see #getBeanNamesForType(Class, boolean, boolean) + */ + ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit); + + /** + * Return a provider for the specified bean, allowing for lazy on-demand retrieval + * of instances, including availability and uniqueness options. + * @param requiredType type the bean must match; can be a generic type declaration. + * Note that collection types are not supported here, in contrast to reflective + * injection points. For programmatically retrieving a list of beans matching a + * specific type, specify the actual bean type as an argument here and subsequently + * use {@link ObjectProvider#orderedStream()} or its lazy streaming/iteration options. + * @param allowEagerInit whether stream-based access may initialize lazy-init + * singletons and objects created by FactoryBeans (or by factory methods + * with a "factory-bean" reference) for the type check + * @return a corresponding provider handle + * @since 5.3 + * @see #getBeanProvider(ResolvableType) + * @see ObjectProvider#iterator() + * @see ObjectProvider#stream() + * @see ObjectProvider#orderedStream() + * @see #getBeanNamesForType(ResolvableType, boolean, boolean) + */ + ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit); + /** * Return the names of beans matching the given type (including subclasses), * judging from either bean definitions or the value of {@code getObjectType} 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 50e537d53e..6731ed5b45 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 @@ -354,14 +354,51 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } @Override - public ObjectProvider getBeanProvider(Class requiredType) throws BeansException { + public ObjectProvider getBeanProvider(Class requiredType) { Assert.notNull(requiredType, "Required type must not be null"); - return getBeanProvider(ResolvableType.forRawClass(requiredType)); + return getBeanProvider(ResolvableType.forRawClass(requiredType), true); } - @SuppressWarnings("unchecked") @Override public ObjectProvider getBeanProvider(ResolvableType requiredType) { + return getBeanProvider(requiredType, true); + } + + + //--------------------------------------------------------------------- + // Implementation of ListableBeanFactory interface + //--------------------------------------------------------------------- + + @Override + public boolean containsBeanDefinition(String beanName) { + Assert.notNull(beanName, "Bean name must not be null"); + return this.beanDefinitionMap.containsKey(beanName); + } + + @Override + public int getBeanDefinitionCount() { + return this.beanDefinitionMap.size(); + } + + @Override + public String[] getBeanDefinitionNames() { + String[] frozenNames = this.frozenBeanDefinitionNames; + if (frozenNames != null) { + return frozenNames.clone(); + } + else { + return StringUtils.toStringArray(this.beanDefinitionNames); + } + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + Assert.notNull(requiredType, "Required type must not be null"); + return getBeanProvider(ResolvableType.forRawClass(requiredType), allowEagerInit); + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { return new BeanObjectProvider() { @Override public T getObject() throws BeansException { @@ -426,14 +463,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } @Override + @SuppressWarnings("unchecked") public Stream stream() { - return Arrays.stream(getBeanNamesForTypedStream(requiredType)) + return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit)) .map(name -> (T) getBean(name)) .filter(bean -> !(bean instanceof NullBean)); } @Override + @SuppressWarnings("unchecked") public Stream orderedStream() { - String[] beanNames = getBeanNamesForTypedStream(requiredType); + String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit); if (beanNames.length == 0) { return Stream.empty(); } @@ -472,35 +511,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return null; } - private String[] getBeanNamesForTypedStream(ResolvableType requiredType) { - return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType); - } - - - //--------------------------------------------------------------------- - // Implementation of ListableBeanFactory interface - //--------------------------------------------------------------------- - - @Override - public boolean containsBeanDefinition(String beanName) { - Assert.notNull(beanName, "Bean name must not be null"); - return this.beanDefinitionMap.containsKey(beanName); - } - - @Override - public int getBeanDefinitionCount() { - return this.beanDefinitionMap.size(); - } - - @Override - public String[] getBeanDefinitionNames() { - String[] frozenNames = this.frozenBeanDefinitionNames; - if (frozenNames != null) { - return frozenNames.clone(); - } - else { - return StringUtils.toStringArray(this.beanDefinitionNames); - } + private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) { + return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit); } @Override diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 25c4cd59c5..a5430120df 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -186,73 +186,12 @@ public class StaticListableBeanFactory implements ListableBeanFactory { @Override public ObjectProvider getBeanProvider(Class requiredType) throws BeansException { - return getBeanProvider(ResolvableType.forRawClass(requiredType)); + return getBeanProvider(ResolvableType.forRawClass(requiredType), true); } - @SuppressWarnings("unchecked") @Override public ObjectProvider getBeanProvider(ResolvableType requiredType) { - return new ObjectProvider() { - @Override - public T getObject() throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0], requiredType); - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(requiredType, beanNames); - } - else { - throw new NoSuchBeanDefinitionException(requiredType); - } - } - @Override - public T getObject(Object... args) throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0], args); - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(requiredType, beanNames); - } - else { - throw new NoSuchBeanDefinitionException(requiredType); - } - } - @Override - @Nullable - public T getIfAvailable() throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0]); - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(requiredType, beanNames); - } - else { - return null; - } - } - @Override - @Nullable - public T getIfUnique() throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0]); - } - else { - return null; - } - } - @Override - public Stream stream() { - return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name)); - } - @Override - public Stream orderedStream() { - return stream().sorted(OrderComparator.INSTANCE); - } - }; + return getBeanProvider(requiredType, true); } @Override @@ -337,6 +276,77 @@ public class StaticListableBeanFactory implements ListableBeanFactory { return StringUtils.toStringArray(this.beans.keySet()); } + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + return getBeanProvider(ResolvableType.forRawClass(requiredType), allowEagerInit); + } + + @SuppressWarnings("unchecked") + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { + return new ObjectProvider() { + @Override + public T getObject() throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0], requiredType); + } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else { + throw new NoSuchBeanDefinitionException(requiredType); + } + } + @Override + public T getObject(Object... args) throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0], args); + } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else { + throw new NoSuchBeanDefinitionException(requiredType); + } + } + @Override + @Nullable + public T getIfAvailable() throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0]); + } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else { + return null; + } + } + @Override + @Nullable + public T getIfUnique() throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0]); + } + else { + return null; + } + } + @Override + public Stream stream() { + return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name)); + } + @Override + public Stream orderedStream() { + return stream().sorted(OrderComparator.INSTANCE); + } + }; + } + @Override public String[] getBeanNamesForType(@Nullable ResolvableType type) { return getBeanNamesForType(type, true, true); diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 951861aa85..b954d56159 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -1263,6 +1263,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader return getBeanFactory().getBeanDefinitionNames(); } + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + assertBeanFactoryActive(); + return getBeanFactory().getBeanProvider(requiredType, allowEagerInit); + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { + assertBeanFactoryActive(); + return getBeanFactory().getBeanProvider(requiredType, allowEagerInit); + } + @Override public String[] getBeanNamesForType(ResolvableType type) { assertBeanFactoryActive(); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java index b0ab174a8f..c5271e8d18 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -252,6 +252,16 @@ class StubWebApplicationContext implements WebApplicationContext { return this.beanFactory.getBeanDefinitionNames(); } + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + return this.beanFactory.getBeanProvider(requiredType, allowEagerInit); + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { + return this.beanFactory.getBeanProvider(requiredType, allowEagerInit); + } + @Override public String[] getBeanNamesForType(@Nullable ResolvableType type) { return this.beanFactory.getBeanNamesForType(type);