diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistry.java index a9beddaf545..3348da86fd8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanRegistry.java @@ -24,7 +24,6 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ResolvableType; import org.springframework.core.env.Environment; @@ -55,43 +54,92 @@ public interface BeanRegistry { void registerAlias(String name, String alias); /** - * Register a bean from the given bean class, which will be instantiated using the + * Register a bean from the given class, which will be instantiated using the * related {@link BeanUtils#getResolvableConstructor resolvable constructor} if any. + *

For registering a bean with a generic type, consider + * {@link #registerBean(ParameterizedTypeReference)}. * @param beanClass the class of the bean * @return the generated bean name + * @see #registerBean(Class) */ String registerBean(Class beanClass); /** - * Register a bean from the given bean class, customizing it with the customizer + * Register a bean from the given generics-containing type, which will be + * instantiated using the related + * {@link BeanUtils#getResolvableConstructor resolvable constructor} if any. + * @param beanType the generics-containing type of the bean + * @return the generated bean name + */ + String registerBean(ParameterizedTypeReference beanType); + + /** + * Register a bean from the given class, customizing it with the customizer * callback. The bean will be instantiated using the supplier that can be configured * in the customizer callback, or will be tentatively instantiated with its * {@link BeanUtils#getResolvableConstructor resolvable constructor} otherwise. + *

For registering a bean with a generic type, consider + * {@link #registerBean(ParameterizedTypeReference, Consumer)}. * @param beanClass the class of the bean - * @param customizer callback to customize other bean properties than the name + * @param customizer the callback to customize other bean properties than the name * @return the generated bean name */ String registerBean(Class beanClass, Consumer> customizer); /** - * Register a bean from the given bean class, which will be instantiated using the + * Register a bean from the given generics-containing type, customizing it + * with the customizer callback. The bean will be instantiated using the supplier + * that can be configured in the customizer callback, or will be tentatively instantiated + * with its {@link BeanUtils#getResolvableConstructor resolvable constructor} otherwise. + * @param beanType the generics-containing type of the bean + * @param customizer the callback to customize other bean properties than the name + * @return the generated bean name + */ + String registerBean(ParameterizedTypeReference beanType, Consumer> customizer); + + /** + * Register a bean from the given class, which will be instantiated using the * related {@link BeanUtils#getResolvableConstructor resolvable constructor} if any. + *

For registering a bean with a generic type, consider + * {@link #registerBean(String, ParameterizedTypeReference)}. * @param name the name of the bean * @param beanClass the class of the bean */ void registerBean(String name, Class beanClass); /** - * Register a bean from the given bean class, customizing it with the customizer + * Register a bean from the given generics-containing type, which + * will be instantiated using the related + * {@link BeanUtils#getResolvableConstructor resolvable constructor} if any. + * @param name the name of the bean + * @param beanType the generics-containing type of the bean + */ + void registerBean(String name, ParameterizedTypeReference beanType); + + /** + * Register a bean from the given class, customizing it with the customizer * callback. The bean will be instantiated using the supplier that can be configured * in the customizer callback, or will be tentatively instantiated with its * {@link BeanUtils#getResolvableConstructor resolvable constructor} otherwise. + *

For registering a bean with a generic type, consider + * {@link #registerBean(String, ParameterizedTypeReference, Consumer)}. * @param name the name of the bean * @param beanClass the class of the bean - * @param customizer callback to customize other bean properties than the name + * @param customizer the callback to customize other bean properties than the name */ void registerBean(String name, Class beanClass, Consumer> customizer); + /** + * Register a bean from the given generics-containing type, customizing it + * with the customizer callback. The bean will be instantiated using the supplier + * that can be configured in the customizer callback, or will be tentatively instantiated + * with its {@link BeanUtils#getResolvableConstructor resolvable constructor} otherwise. + * @param name the name of the bean + * @param beanType the generics-containing type of the bean + * @param customizer the callback to customize other bean properties than the name + */ + void registerBean(String name, ParameterizedTypeReference beanType, Consumer> customizer); + /** * Specification for customizing a bean. @@ -164,20 +212,6 @@ public interface BeanRegistry { * @see AbstractBeanDefinition#setInstanceSupplier(Supplier) */ Spec supplier(Function supplier); - - /** - * Set a generics-containing target type of this bean. - * @see #targetType(ResolvableType) - * @see RootBeanDefinition#setTargetType(ResolvableType) - */ - Spec targetType(ParameterizedTypeReference type); - - /** - * Set a generics-containing target type of this bean. - * @see #targetType(ParameterizedTypeReference) - * @see RootBeanDefinition#setTargetType(ResolvableType) - */ - Spec targetType(ResolvableType type); } @@ -188,32 +222,40 @@ public interface BeanRegistry { interface SupplierContext { /** - * Return the bean instance that uniquely matches the given object type, if any. - * @param requiredType type the bean must match; can be an interface or superclass - * @return an instance of the single bean matching the required type + * Return the bean instance that uniquely matches the given type, if any. + * @param beanClass the type the bean must match; can be an interface or superclass + * @return an instance of the single bean matching the bean type + * @see BeanFactory#getBean(String) + */ + T bean(Class beanClass) throws BeansException; + + /** + * Return the bean instance that uniquely matches the given generics-containing type, if any. + * @param beanType the generics-containing type the bean must match; can be an interface or superclass + * @return an instance of the single bean matching the bean type * @see BeanFactory#getBean(String) */ - T bean(Class requiredType) throws BeansException; + T bean(ParameterizedTypeReference beanType) throws BeansException; /** * Return an instance, which may be shared or independent, of the * specified bean. * @param name the name of the bean to retrieve - * @param requiredType type the bean must match; can be an interface or superclass + * @param beanClass the type the bean must match; can be an interface or superclass * @return an instance of the bean. * @see BeanFactory#getBean(String, Class) */ - T bean(String name, Class requiredType) throws BeansException; + T bean(String name, Class beanClass) throws BeansException; /** * Return a provider for the specified bean, allowing for lazy on-demand retrieval * of instances, including availability and uniqueness options. - *

For matching a generic type, consider {@link #beanProvider(ResolvableType)}. - * @param requiredType type the bean must match; can be an interface or superclass + *

For matching a generic type, consider {@link #beanProvider(ParameterizedTypeReference)}. + * @param beanClass the type the bean must match; can be an interface or superclass * @return a corresponding provider handle * @see BeanFactory#getBeanProvider(Class) */ - ObjectProvider beanProvider(Class requiredType); + ObjectProvider beanProvider(Class beanClass); /** * Return a provider for the specified bean, allowing for lazy on-demand retrieval @@ -229,11 +271,11 @@ public interface BeanRegistry { * Java compiler warning), consider calling {@link #beanProvider(Class)} with the * raw type as a second step if no full generic match is * {@link ObjectProvider#getIfAvailable() available} with this variant. - * @param requiredType type the bean must match; can be a generic type declaration + * @param beanType the generics-containing type the bean must match; can be an interface or superclass * @return a corresponding provider handle * @see BeanFactory#getBeanProvider(ResolvableType) */ - ObjectProvider beanProvider(ResolvableType requiredType); + ObjectProvider beanProvider(ParameterizedTypeReference beanType); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java index 9131786e531..103c59dd2f5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java @@ -17,6 +17,7 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Constructor; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; @@ -92,6 +93,14 @@ public class BeanRegistryAdapter implements BeanRegistry { return beanName; } + @Override + public String registerBean(ParameterizedTypeReference beanType) { + ResolvableType resolvableType = ResolvableType.forType(beanType); + String beanName = BeanDefinitionReaderUtils.uniqueBeanName(Objects.requireNonNull(resolvableType.resolve()).getName(), this.beanRegistry); + registerBean(beanName, beanType); + return beanName; + } + @Override public String registerBean(Class beanClass, Consumer> customizer) { String beanName = BeanDefinitionReaderUtils.uniqueBeanName(beanClass.getName(), this.beanRegistry); @@ -99,6 +108,15 @@ public class BeanRegistryAdapter implements BeanRegistry { return beanName; } + @Override + public String registerBean(ParameterizedTypeReference beanType, Consumer> customizer) { + ResolvableType resolvableType = ResolvableType.forType(beanType); + Class beanClass = Objects.requireNonNull(resolvableType.resolve()); + String beanName = BeanDefinitionReaderUtils.uniqueBeanName(beanClass.getName(), this.beanRegistry); + registerBean(beanName, beanType, customizer); + return beanName; + } + @Override public void registerBean(String name, Class beanClass) { BeanRegistrarBeanDefinition beanDefinition = new BeanRegistrarBeanDefinition(beanClass, this.beanRegistrarClass); @@ -111,9 +129,11 @@ public class BeanRegistryAdapter implements BeanRegistry { } @Override - public void registerBean(String name, Class beanClass, Consumer> spec) { + public void registerBean(String name, ParameterizedTypeReference beanType) { + ResolvableType resolvableType = ResolvableType.forType(beanType); + Class beanClass = Objects.requireNonNull(resolvableType.resolve()); BeanRegistrarBeanDefinition beanDefinition = new BeanRegistrarBeanDefinition(beanClass, this.beanRegistrarClass); - spec.accept(new BeanSpecAdapter<>(beanDefinition, this.beanFactory)); + beanDefinition.setTargetType(resolvableType); if (this.customizers != null && this.customizers.containsKey(name)) { for (BeanDefinitionCustomizer customizer : this.customizers.get(name)) { customizer.customize(beanDefinition); @@ -122,6 +142,33 @@ public class BeanRegistryAdapter implements BeanRegistry { this.beanRegistry.registerBeanDefinition(name, beanDefinition); } + @Override + public void registerBean(String name, Class beanClass, Consumer> customizer) { + BeanRegistrarBeanDefinition beanDefinition = new BeanRegistrarBeanDefinition(beanClass, this.beanRegistrarClass); + customizer.accept(new BeanSpecAdapter<>(beanDefinition, this.beanFactory)); + if (this.customizers != null && this.customizers.containsKey(name)) { + for (BeanDefinitionCustomizer registryCustomizer : this.customizers.get(name)) { + registryCustomizer.customize(beanDefinition); + } + } + this.beanRegistry.registerBeanDefinition(name, beanDefinition); + } + + @Override + public void registerBean(String name, ParameterizedTypeReference beanType, Consumer> customizer) { + ResolvableType resolvableType = ResolvableType.forType(beanType); + Class beanClass = Objects.requireNonNull(resolvableType.resolve()); + BeanRegistrarBeanDefinition beanDefinition = new BeanRegistrarBeanDefinition(beanClass, this.beanRegistrarClass); + beanDefinition.setTargetType(resolvableType); + customizer.accept(new BeanSpecAdapter<>(beanDefinition, this.beanFactory)); + if (this.customizers != null && this.customizers.containsKey(name)) { + for (BeanDefinitionCustomizer registryCustomizer : this.customizers.get(name)) { + registryCustomizer.customize(beanDefinition); + } + } + this.beanRegistry.registerBeanDefinition(name, beanDefinition); + } + @Override public void register(BeanRegistrar registrar) { Assert.notNull(registrar, "'registrar' must not be null"); @@ -238,18 +285,6 @@ public class BeanRegistryAdapter implements BeanRegistry { supplier.apply(new SupplierContextAdapter(this.beanFactory))); return this; } - - @Override - public Spec targetType(ParameterizedTypeReference targetType) { - this.beanDefinition.setTargetType(ResolvableType.forType(targetType)); - return this; - } - - @Override - public Spec targetType(ResolvableType targetType) { - this.beanDefinition.setTargetType(targetType); - return this; - } } @@ -262,23 +297,28 @@ public class BeanRegistryAdapter implements BeanRegistry { } @Override - public T bean(Class requiredType) throws BeansException { - return this.beanFactory.getBean(requiredType); + public T bean(Class beanClass) throws BeansException { + return this.beanFactory.getBean(beanClass); + } + + @Override + public T bean(ParameterizedTypeReference beanType) throws BeansException { + return this.beanFactory.getBeanProvider(beanType).getObject(); } @Override - public T bean(String name, Class requiredType) throws BeansException { - return this.beanFactory.getBean(name, requiredType); + public T bean(String name, Class beanClass) throws BeansException { + return this.beanFactory.getBean(name, beanClass); } @Override - public ObjectProvider beanProvider(Class requiredType) { - return this.beanFactory.getBeanProvider(requiredType); + public ObjectProvider beanProvider(Class beanClass) { + return this.beanFactory.getBeanProvider(beanClass); } @Override - public ObjectProvider beanProvider(ResolvableType requiredType) { - return this.beanFactory.getBeanProvider(requiredType); + public ObjectProvider beanProvider(ParameterizedTypeReference beanType) { + return this.beanFactory.getBeanProvider(beanType); } } diff --git a/spring-beans/src/main/kotlin/org/springframework/beans/factory/BeanRegistrarDsl.kt b/spring-beans/src/main/kotlin/org/springframework/beans/factory/BeanRegistrarDsl.kt index a6a4a56a64f..7deae838654 100644 --- a/spring-beans/src/main/kotlin/org/springframework/beans/factory/BeanRegistrarDsl.kt +++ b/spring-beans/src/main/kotlin/org/springframework/beans/factory/BeanRegistrarDsl.kt @@ -168,12 +168,8 @@ open class BeanRegistrarDsl(private val init: BeanRegistrarDsl.() -> Unit): Bean if (prototype) { it.prototype() } - val resolvableType = ResolvableType.forType(object: ParameterizedTypeReference() {}); - if (resolvableType.hasGenerics()) { - it.targetType(resolvableType) - } } - registry.registerBean(name, T::class.java, customizer) + registry.registerBean(name, object: ParameterizedTypeReference() {}, customizer) } /** @@ -234,12 +230,8 @@ open class BeanRegistrarDsl(private val init: BeanRegistrarDsl.() -> Unit): Bean if (prototype) { it.prototype() } - val resolvableType = ResolvableType.forType(object: ParameterizedTypeReference() {}); - if (resolvableType.hasGenerics()) { - it.targetType(resolvableType) - } } - return registry.registerBean(T::class.java, customizer) + return registry.registerBean(object: ParameterizedTypeReference() {}, customizer) } /** @@ -304,12 +296,8 @@ open class BeanRegistrarDsl(private val init: BeanRegistrarDsl.() -> Unit): Bean it.supplier { SupplierContextDsl(it, env).supplier() } - val resolvableType = ResolvableType.forType(object: ParameterizedTypeReference() {}); - if (resolvableType.hasGenerics()) { - it.targetType(resolvableType) - } } - registry.registerBean(name, T::class.java, customizer) + registry.registerBean(name, object: ParameterizedTypeReference() {}, customizer) } inline fun registerBean(autowirable: Boolean = true, @@ -372,12 +360,8 @@ open class BeanRegistrarDsl(private val init: BeanRegistrarDsl.() -> Unit): Bean it.supplier { SupplierContextDsl(it, env).supplier() } - val resolvableType = ResolvableType.forType(object: ParameterizedTypeReference() {}); - if (resolvableType.hasGenerics()) { - it.targetType(resolvableType) - } } - return registry.registerBean(T::class.java, customizer) + return registry.registerBean(object: ParameterizedTypeReference() {}, customizer) } // Function with 0 parameter @@ -1094,7 +1078,7 @@ open class BeanRegistrarDsl(private val init: BeanRegistrarDsl.() -> Unit): Bean * @return a corresponding provider handle */ inline fun beanProvider() : ObjectProvider = - context.beanProvider(ResolvableType.forType((object : ParameterizedTypeReference() {}).type)) + context.beanProvider(object : ParameterizedTypeReference() {}) } override fun register(registry: BeanRegistry, env: Environment) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanRegistryAdapterTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanRegistryAdapterTests.java index 51461235a88..c1ceba10796 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanRegistryAdapterTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanRegistryAdapterTests.java @@ -24,7 +24,6 @@ import org.springframework.beans.factory.BeanRegistrar; import org.springframework.beans.factory.BeanRegistry; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.ResolvableType; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; @@ -206,18 +205,10 @@ public class BeanRegistryAdapterTests { } @Test - void customTargetTypeFromResolvableType() { - BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, env, TargetTypeBeanRegistrar.class); - new TargetTypeBeanRegistrar().register(adapter, env); - RootBeanDefinition beanDefinition = (RootBeanDefinition)this.beanFactory.getBeanDefinition("fooSupplierFromResolvableType"); - assertThat(beanDefinition.getResolvableType().resolveGeneric(0)).isEqualTo(Foo.class); - } - - @Test - void customTargetTypeFromTypeReference() { - BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, env, TargetTypeBeanRegistrar.class); - new TargetTypeBeanRegistrar().register(adapter, env); - RootBeanDefinition beanDefinition = (RootBeanDefinition)this.beanFactory.getBeanDefinition("fooSupplierFromTypeReference"); + void genericType() { + BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, env, GenericTypeBeanRegistrar.class); + new GenericTypeBeanRegistrar().register(adapter, env); + RootBeanDefinition beanDefinition = (RootBeanDefinition)this.beanFactory.getBeanDefinition("fooSupplier"); assertThat(beanDefinition.getResolvableType().resolveGeneric(0)).isEqualTo(Foo.class); } @@ -325,15 +316,11 @@ public class BeanRegistryAdapterTests { } } - private static class TargetTypeBeanRegistrar implements BeanRegistrar { + private static class GenericTypeBeanRegistrar implements BeanRegistrar { @Override public void register(BeanRegistry registry, Environment env) { - registry.registerBean("fooSupplierFromResolvableType", Foo.class, - spec -> spec.targetType(ResolvableType.forClassWithGenerics(Supplier.class, Foo.class))); - ParameterizedTypeReference> type = new ParameterizedTypeReference<>() {}; - registry.registerBean("fooSupplierFromTypeReference", Supplier.class, - spec -> spec.targetType(type)); + registry.registerBean("fooSupplier", new ParameterizedTypeReference>() {}); } } diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/GenericBeanRegistrar.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/GenericBeanRegistrar.java index 4f1801d3034..b732d9a2179 100644 --- a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/GenericBeanRegistrar.java +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/GenericBeanRegistrar.java @@ -27,9 +27,8 @@ public class GenericBeanRegistrar implements BeanRegistrar { @Override public void register(BeanRegistry registry, Environment env) { - ParameterizedTypeReference> type = new ParameterizedTypeReference<>() {}; - registry.registerBean("fooSupplier", Supplier.class, spec -> spec.targetType(type) - .supplier(context-> (Supplier) Foo::new)); + registry.registerBean("fooSupplier", new ParameterizedTypeReference>() {}, spec -> + spec.supplier(context-> (Supplier) Foo::new)); } public record Foo() {}