diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index c1b309154da..ad47efb2b2a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -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 { */ ObjectProvider 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. + *

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) + */ + ObjectProvider getBeanProvider(ParameterizedTypeReference requiredType); + /** * Does this bean factory contain a bean definition or externally registered singleton * instance with the given name? 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 a223d2261aa..b429fcbcf0a 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 @@ -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 ObjectProvider getBeanProvider(ParameterizedTypeReference requiredType) { + return getBeanProvider(ResolvableType.forType(requiredType), true); + } + //--------------------------------------------------------------------- // Implementation of ListableBeanFactory interface 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 26f19262fc9..3b2c5e9c189 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 @@ -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 ObjectProvider getBeanProvider(ParameterizedTypeReference requiredType) { + return getBeanProvider(ResolvableType.forType(requiredType), true); + } + @Override public boolean containsBean(String name) { return this.beans.containsKey(name); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java index 716afff4845..c8b75134a3c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java @@ -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> numberStoreProvider = bf.getBeanProvider(ResolvableType.forClass(NumberStore.class)); - ObjectProvider> doubleStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Double.class)); - ObjectProvider> floatStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Float.class)); + ObjectProvider> numberStoreProvider = bf.getBeanProvider(new ParameterizedTypeReference<>() {}); + ObjectProvider> doubleStoreProvider = bf.getBeanProvider(new ParameterizedTypeReference<>() {}); + ObjectProvider> floatStoreProvider = bf.getBeanProvider(new ParameterizedTypeReference<>() {}); assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(numberStoreProvider::getObject); assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(numberStoreProvider::getIfAvailable); assertThat(numberStoreProvider.getIfUnique()).isNull(); 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 96028c17e64..4afe9f1ba9f 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 @@ -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 ObjectProvider getBeanProvider(ParameterizedTypeReference requiredType) { + assertBeanFactoryActive(); + return getBeanFactory().getBeanProvider(requiredType); + } + @Override public boolean containsBean(String name) { return getBeanFactory().containsBean(name); diff --git a/spring-context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java b/spring-context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java index 29d85450387..cd6f5feb498 100644 --- a/spring-context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java +++ b/spring-context/src/main/java/org/springframework/jndi/support/SimpleJndiBeanFactory.java @@ -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 ObjectProvider getBeanProvider(ParameterizedTypeReference 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)) { 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 38eba96f662..678db294465 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 @@ -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 ObjectProvider getBeanProvider(ParameterizedTypeReference requiredType) { + return this.beanFactory.getBeanProvider(requiredType); + } + @Override public boolean containsBean(String name) { return this.beanFactory.containsBean(name);