Add getBeanProvider(ParameterizedTypeReference) overload

Closes gh-31444
This commit is contained in:
Juergen Hoeller
2025-10-15 16:10:28 +02:00
parent 6503f35404
commit 2e4ca41382
7 changed files with 52 additions and 3 deletions
@@ -19,6 +19,7 @@ package org.springframework.beans.factory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
/**
@@ -264,6 +265,22 @@ public interface BeanFactory {
*/
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
/**
* Return a provider for the specified bean, allowing for lazy on-demand retrieval
* of instances, including availability and uniqueness options. This variant allows
* for specifying a generic type to match, similar to reflective injection points
* with generic type declarations in method/constructor parameters.
* <p>This is a variant of {@link #getBeanProvider(ResolvableType)} with a
* captured generic type for type-safe retrieval, typically used inline:
* {@code getBeanProvider(new ParameterizedTypeReference<>() {})} - and
* effectively equivalent to {@code getBeanProvider(ResolvableType.forType(...))}.
* @return a corresponding provider handle
* @param requiredType a captured generic type that the bean must match
* @since 7.0
* @see #getBeanProvider(ResolvableType)
*/
<T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType);
/**
* Does this bean factory contain a bean definition or externally registered singleton
* instance with the given name?
@@ -78,6 +78,7 @@ import org.springframework.beans.factory.config.NamedBeanHolder;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.MergedAnnotation;
@@ -398,6 +399,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return getBeanProvider(requiredType, true);
}
public <T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType) {
return getBeanProvider(ResolvableType.forType(requiredType), true);
}
//---------------------------------------------------------------------
// Implementation of ListableBeanFactory interface
@@ -39,6 +39,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.Assert;
@@ -199,6 +200,11 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
return getBeanProvider(requiredType, true);
}
@Override
public <T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType) {
return getBeanProvider(ResolvableType.forType(requiredType), true);
}
@Override
public boolean containsBean(String name) {
return this.beans.containsKey(name);
@@ -45,6 +45,7 @@ import org.springframework.beans.testfixture.beans.GenericIntegerBean;
import org.springframework.beans.testfixture.beans.GenericSetOfIntegerBean;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
@@ -792,9 +793,9 @@ class BeanFactoryGenericsTests {
assertThat(doubleStoreNames).containsExactly("store1");
assertThat(floatStoreNames).containsExactly("store2");
ObjectProvider<NumberStore<?>> numberStoreProvider = bf.getBeanProvider(ResolvableType.forClass(NumberStore.class));
ObjectProvider<NumberStore<Double>> doubleStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Double.class));
ObjectProvider<NumberStore<Float>> floatStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Float.class));
ObjectProvider<NumberStore<?>> numberStoreProvider = bf.getBeanProvider(new ParameterizedTypeReference<>() {});
ObjectProvider<NumberStore<Double>> doubleStoreProvider = bf.getBeanProvider(new ParameterizedTypeReference<>() {});
ObjectProvider<NumberStore<Float>> floatStoreProvider = bf.getBeanProvider(new ParameterizedTypeReference<>() {});
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(numberStoreProvider::getObject);
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(numberStoreProvider::getIfAvailable);
assertThat(numberStoreProvider.getIfUnique()).isNull();
@@ -76,6 +76,7 @@ import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
import org.springframework.core.NativeDetector;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
@@ -1303,6 +1304,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return getBeanFactory().getBeanProvider(requiredType);
}
@Override
public <T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType) {
assertBeanFactoryActive();
return getBeanFactory().getBeanProvider(requiredType);
}
@Override
public boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
@@ -34,6 +34,7 @@ import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.jndi.JndiLocatorSupport;
import org.springframework.jndi.TypeMismatchNamingException;
@@ -195,6 +196,12 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
"SimpleJndiBeanFactory does not support resolution by ResolvableType");
}
@Override
public <T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType) {
throw new UnsupportedOperationException(
"SimpleJndiBeanFactory does not support resolution by ParameterizedTypeReference");
}
@Override
public boolean containsBean(String name) {
if (this.singletonObjects.containsKey(name) || this.resourceTypes.containsKey(name)) {
@@ -43,6 +43,7 @@ import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.DelegatingMessageSource;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
@@ -192,6 +193,11 @@ class StubWebApplicationContext implements WebApplicationContext {
return this.beanFactory.getBeanProvider(requiredType);
}
@Override
public <T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType) {
return this.beanFactory.getBeanProvider(requiredType);
}
@Override
public boolean containsBean(String name) {
return this.beanFactory.containsBean(name);