Introduce first-class support for programmatic bean registration

This commit introduces a new BeanRegistrar interface that can be
implemented to register beans programmatically in a concise and
flexible way.

Those bean registrar implementations are typically imported with
an `@Import` annotation on `@Configuration` classes.

See BeanRegistrarConfigurationTests for a concrete example.

See gh-18353
This commit is contained in:
Sébastien Deleuze
2025-03-06 18:51:09 +01:00
parent aeaf52ee96
commit 496be9ca98
14 changed files with 1356 additions and 8 deletions

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2025 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory;
import org.springframework.core.env.Environment;
/**
* Contract for registering beans programmatically.
*
* <p>Typically imported with an {@link org.springframework.context.annotation.Import @Import}
* annotation on {@link org.springframework.context.annotation.Configuration @Configuration}
* classes.
* <pre class="code">
* &#064;Configuration
* &#064;Import(MyBeanRegistrar.class)
* class MyConfiguration {
* }</pre>
*
* <p>The bean registrar implementation uses {@link BeanRegistry} and {@link Environment}
* APIs to register beans programmatically in a concise and flexible way.
* <pre class="code">
* class MyBeanRegistrar implements BeanRegistrar {
*
* &#064;Override
* public void register(BeanRegistry registry, Environment env) {
* registry.registerBean("foo", Foo.class);
* registry.registerBean("bar", Bar.class, spec -> spec
* .prototype()
* .lazyInit()
* .description("Custom description")
* .supplier(context -> new Bar(context.bean(Foo.class))));
* if (env.matchesProfiles("baz")) {
* registry.registerBean(Baz.class, spec -> spec
* .supplier(context -> new Baz("Hello World!")));
* }
* }
* }</pre>
*
* <p>In Kotlin, it is recommended to use {@code BeanRegistrarDsl} instead of
* implementing {@code BeanRegistrar}.
*
* @author Sebastien Deleuze
* @since 7.0
*/
@FunctionalInterface
public interface BeanRegistrar {
/**
* Register beans in a programmatic way.
* @param registry the bean registry
* @param env the environment that can be used to get the active profile or some properties
*/
void register(BeanRegistry registry, Environment env);
}

View File

@@ -0,0 +1,210 @@
/*
* Copyright 2002-2025 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
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.core.ResolvableType;
import org.springframework.core.env.Environment;
/**
* Used in {@link BeanRegistrar#register(BeanRegistry, Environment)} to expose
* programmatic bean registration capabilities.
*
* @author Sebastien Deleuze
* @since 7.0
*/
public interface BeanRegistry {
/**
* Register a bean from the given bean class, which will be instantiated
* using the related {@link BeanUtils#getResolvableConstructor resolvable constructor}
* if any.
* @param beanClass the class of the bean
* @return the generated bean name
*/
<T> String registerBean(Class<T> beanClass);
/**
* Register a bean from the given bean 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.
* @param beanClass the class of the bean
* @param customizer callback to customize other bean properties than the name
* @return the generated bean name
*/
<T> String registerBean(Class<T> beanClass, Consumer<Spec<T>> customizer);
/**
* Register a bean from the given bean class, which will be instantiated
* using the related {@link BeanUtils#getResolvableConstructor resolvable constructor}
* if any.
* @param name the name of the bean
* @param beanClass the class of the bean
*/
<T> void registerBean(String name, Class<T> beanClass);
/**
* Register a bean from the given bean 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.
* @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
*/
<T> void registerBean(String name, Class<T> beanClass, Consumer<Spec<T>> customizer);
/**
* Specification for customizing a bean.
* @param <T> the bean type
*/
interface Spec<T> {
/**
* Allow for instantiating this bean on a background thread.
* @see AbstractBeanDefinition#setBackgroundInit(boolean)
*/
Spec<T> backgroundInit();
/**
* Set a human-readable description of this bean.
* @see BeanDefinition#setDescription(String)
*/
Spec<T> description(String description);
/**
* Configure this bean as a fallback autowire candidate.
* @see BeanDefinition#setFallback(boolean)
* @see #primary
*/
Spec<T> fallback();
/**
* Hint that this bean has an infrastructure role, meaning it has no
* relevance to the end-user.
* @see BeanDefinition#setRole(int)
* @see BeanDefinition#ROLE_INFRASTRUCTURE
*/
Spec<T> infrastructure();
/**
* Configure this bean as lazily initialized.
* @see BeanDefinition#setLazyInit(boolean)
*/
Spec<T> lazyInit();
/**
* Configure this bean as not a candidate for getting autowired into some
* other bean.
* @see BeanDefinition#setAutowireCandidate(boolean)
*/
Spec<T> notAutowirable();
/**
* The sort order of this bean. This is analogous to the
* {@code @Order} annotation.
* @see AbstractBeanDefinition#ORDER_ATTRIBUTE
*/
Spec<T> order(int order);
/**
* Configure this bean as a primary autowire candidate.
* @see BeanDefinition#setPrimary(boolean)
* @see #fallback
*/
Spec<T> primary();
/**
* Configure this bean with a prototype scope.
* @see BeanDefinition#setScope(String)
* @see BeanDefinition#SCOPE_PROTOTYPE
*/
Spec<T> prototype();
/**
* Set the supplier to construct a bean instance.
* @see AbstractBeanDefinition#setInstanceSupplier(Supplier)
*/
Spec<T> supplier(Function<SupplierContext, T> supplier);
}
/**
* Context available from the bean instance supplier designed to give access
* to bean dependencies.
*/
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
* @see BeanFactory#getBean(String)
*/
<T> T bean(Class<T> requiredType) 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
* @return an instance of the bean.
* @see BeanFactory#getBean(String, Class)
*/
<T> T bean(String name, Class<T> requiredType) throws BeansException;
/**
* Return a provider for the specified bean, allowing for lazy on-demand retrieval
* of instances, including availability and uniqueness options.
* <p>For matching a generic type, consider {@link #beanProvider(ResolvableType)}.
* @param requiredType type the bean must match; can be an interface or superclass
* @return a corresponding provider handle
* @see BeanFactory#getBeanProvider(Class)
*/
<T> ObjectProvider<T> beanProvider(Class<T> 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>Note that collections of beans 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.
* <p>Also, generics matching is strict here, as per the Java assignment rules.
* For lenient fallback matching with unchecked semantics (similar to the 'unchecked'
* 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
* @return a corresponding provider handle
* @see BeanFactory#getBeanProvider(ResolvableType)
*/
<T> ObjectProvider<T> beanProvider(ResolvableType requiredType);
}
}

View File

@@ -0,0 +1,245 @@
/*
* Copyright 2002-2025 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.support;
import java.lang.reflect.Constructor;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.BeanRegistry;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.core.ResolvableType;
import org.springframework.util.MultiValueMap;
/**
* {@link BeanRegistry} implementation that delegates to
* {@link BeanDefinitionRegistry} and {@link ListableBeanFactory}.
*
* @author Sebastien Deleuze
* @since 7.0
*/
public class BeanRegistryAdapter implements BeanRegistry {
private final BeanDefinitionRegistry beanRegistry;
private final ListableBeanFactory beanFactory;
private final Class<? extends BeanRegistrar> beanRegistrarClass;
private final @Nullable MultiValueMap<String, BeanDefinitionCustomizer> customizers;
public BeanRegistryAdapter(BeanDefinitionRegistry beanRegistry, ListableBeanFactory beanFactory,
Class<? extends BeanRegistrar> beanRegistrarClass) {
this(beanRegistry, beanFactory, beanRegistrarClass, null);
}
public BeanRegistryAdapter(BeanDefinitionRegistry beanRegistry, ListableBeanFactory beanFactory,
Class<? extends BeanRegistrar> beanRegistrarClass, @Nullable MultiValueMap<String, BeanDefinitionCustomizer> customizers) {
this.beanRegistry = beanRegistry;
this.beanFactory = beanFactory;
this.beanRegistrarClass = beanRegistrarClass;
this.customizers = customizers;
}
@Override
public <T> String registerBean(Class<T> beanClass) {
String beanName = BeanDefinitionReaderUtils.uniqueBeanName(beanClass.getName(), this.beanRegistry);
registerBean(beanName, beanClass);
return beanName;
}
@Override
public <T> String registerBean(Class<T> beanClass, Consumer<Spec<T>> customizer) {
String beanName = BeanDefinitionReaderUtils.uniqueBeanName(beanClass.getName(), this.beanRegistry);
registerBean(beanName, beanClass, customizer);
return beanName;
}
@Override
public <T> void registerBean(String name, Class<T> beanClass) {
BeanRegistrarBeanDefinition beanDefinition = new BeanRegistrarBeanDefinition(beanClass, this.beanRegistrarClass);
if (this.customizers != null && this.customizers.containsKey(name)) {
for (BeanDefinitionCustomizer customizer : this.customizers.get(name)) {
customizer.customize(beanDefinition);
}
}
this.beanRegistry.registerBeanDefinition(name, beanDefinition);
}
@Override
public <T> void registerBean(String name, Class<T> beanClass, Consumer<Spec<T>> spec) {
BeanRegistrarBeanDefinition beanDefinition = new BeanRegistrarBeanDefinition(beanClass, this.beanRegistrarClass);
spec.accept(new BeanSpecAdapter<>(beanDefinition, this.beanFactory));
if (this.customizers != null && this.customizers.containsKey(name)) {
for (BeanDefinitionCustomizer customizer : this.customizers.get(name)) {
customizer.customize(beanDefinition);
}
}
this.beanRegistry.registerBeanDefinition(name, beanDefinition);
}
/**
* {@link RootBeanDefinition} subclass for {@code #registerBean} based
* registrations with constructors resolution match{@link BeanUtils#getResolvableConstructor}
* behavior. It also sets the bean registrar class as the source.
*/
@SuppressWarnings("serial")
private static class BeanRegistrarBeanDefinition extends RootBeanDefinition {
public BeanRegistrarBeanDefinition(Class<?> beanClass, Class<? extends BeanRegistrar> beanRegistrarClass) {
super(beanClass);
this.setSource(beanRegistrarClass);
this.setAttribute("aotProcessingIgnoreRegistration", true);
}
public BeanRegistrarBeanDefinition(BeanRegistrarBeanDefinition original) {
super(original);
}
@Override
public Constructor<?> @Nullable [] getPreferredConstructors() {
if (this.getInstanceSupplier() != null) {
return null;
}
try {
return new Constructor<?>[] { BeanUtils.getResolvableConstructor(getBeanClass()) };
}
catch (IllegalStateException ex) {
return null;
}
}
@Override
public RootBeanDefinition cloneBeanDefinition() {
return new BeanRegistrarBeanDefinition(this);
}
}
static class BeanSpecAdapter<T> implements Spec<T> {
private final RootBeanDefinition beanDefinition;
private final ListableBeanFactory beanFactory;
public BeanSpecAdapter(RootBeanDefinition beanDefinition, ListableBeanFactory beanFactory) {
this.beanDefinition = beanDefinition;
this.beanFactory = beanFactory;
}
@Override
public Spec<T> backgroundInit() {
this.beanDefinition.setBackgroundInit(true);
return this;
}
@Override
public Spec<T> fallback() {
this.beanDefinition.setFallback(true);
return this;
}
@Override
public Spec<T> primary() {
this.beanDefinition.setPrimary(true);
return this;
}
@Override
public Spec<T> description(String description) {
this.beanDefinition.setDescription(description);
return this;
}
@Override
public Spec<T> infrastructure() {
this.beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return this;
}
@Override
public Spec<T> lazyInit() {
this.beanDefinition.setLazyInit(true);
return this;
}
@Override
public Spec<T> notAutowirable() {
this.beanDefinition.setAutowireCandidate(false);
return this;
}
@Override
public Spec<T> order(int order) {
this.beanDefinition.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, order);
return this;
}
@Override
public Spec<T> prototype() {
this.beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
return this;
}
@Override
public Spec<T> supplier(Function<SupplierContext, T> supplier) {
this.beanDefinition.setInstanceSupplier(() ->
supplier.apply(new SupplierContextAdapter(this.beanFactory)));
return this;
}
}
static class SupplierContextAdapter implements SupplierContext {
private final ListableBeanFactory beanFactory;
public SupplierContextAdapter(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public <T> T bean(Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(requiredType);
}
@Override
public <T> T bean(String name, Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(name, requiredType);
}
@Override
public <T> ObjectProvider<T> beanProvider(Class<T> requiredType) {
return this.beanFactory.getBeanProvider(requiredType);
}
@Override
public <T> ObjectProvider<T> beanProvider(ResolvableType requiredType) {
return this.beanFactory.getBeanProvider(requiredType);
}
}
}

View File

@@ -0,0 +1,297 @@
/*
* Copyright 2002-2025 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.support;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.BeanRegistry;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link BeanRegistryAdapter}.
*
* @author Sebastien Deleuze
*/
public class BeanRegistryAdapterTests {
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
private final Environment env = new StandardEnvironment();
@Test
void defaultBackgroundInit() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isBackgroundInit()).isFalse();
}
@Test
void enableBackgroundInit() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, BackgroundInitBeanRegistrar.class);
new BackgroundInitBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isBackgroundInit()).isTrue();
}
@Test
void defaultDescription() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getDescription()).isNull();
}
@Test
void customDescription() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, CustomDescriptionBeanRegistrar.class);
new CustomDescriptionBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getDescription()).isEqualTo("custom");
}
@Test
void defaultFallback() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isFallback()).isFalse();
}
@Test
void enableFallback() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, FallbackBeanRegistrar.class);
new FallbackBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isFallback()).isTrue();
}
@Test
void defaultRole() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getRole()).isEqualTo(AbstractBeanDefinition.ROLE_APPLICATION);
}
@Test
void infrastructureRole() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, InfrastructureBeanRegistrar.class);
new InfrastructureBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getRole()).isEqualTo(AbstractBeanDefinition.ROLE_INFRASTRUCTURE);
}
@Test
void defaultLazyInit() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isLazyInit()).isFalse();
}
@Test
void enableLazyInit() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, LazyInitBeanRegistrar.class);
new LazyInitBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isLazyInit()).isTrue();
}
@Test
void defaultAutowirable() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isAutowireCandidate()).isTrue();
}
@Test
void notAutowirable() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, NotAutowirableBeanRegistrar.class);
new NotAutowirableBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isAutowireCandidate()).isFalse();
}
@Test
void defaultOrder() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
Integer order = (Integer)beanDefinition.getAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE);
assertThat(order).isNull();
}
@Test
void customOrder() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, CustomOrderBeanRegistrar.class);
new CustomOrderBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) this.beanFactory.getBeanDefinition("foo");
Integer order = (Integer)beanDefinition.getAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE);
assertThat(order).isEqualTo(1);
}
@Test
void defaultPrimary() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isPrimary()).isFalse();
}
@Test
void enablePrimary() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, PrimaryBeanRegistrar.class);
new PrimaryBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.isPrimary()).isTrue();
}
@Test
void defaultScope() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getScope()).isEqualTo(AbstractBeanDefinition.SCOPE_DEFAULT);
}
@Test
void prototypeScope() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, PrototypeBeanRegistrar.class);
new PrototypeBeanRegistrar().register(adapter, env);
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getScope()).isEqualTo(AbstractBeanDefinition.SCOPE_PROTOTYPE);
}
@Test
void defaultSupplier() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, DefaultBeanRegistrar.class);
new DefaultBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)this.beanFactory.getBeanDefinition("foo");
assertThat(beanDefinition.getInstanceSupplier()).isNull();
}
@Test
void customSupplier() {
BeanRegistryAdapter adapter = new BeanRegistryAdapter(this.beanFactory, this.beanFactory, SupplierBeanRegistrar.class);
new SupplierBeanRegistrar().register(adapter, env);
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)this.beanFactory.getBeanDefinition("foo");
Supplier<?> supplier = beanDefinition.getInstanceSupplier();
assertThat(supplier).isNotNull();
assertThat(supplier.get()).isNotNull().isInstanceOf(Foo.class);
}
private static class DefaultBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class);
}
}
private static class BackgroundInitBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::backgroundInit);
}
}
private static class CustomDescriptionBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, spec -> spec.description("custom"));
}
}
private static class FallbackBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::fallback);
}
}
private static class InfrastructureBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::infrastructure);
}
}
private static class LazyInitBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::lazyInit);
}
}
private static class NotAutowirableBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::notAutowirable);
}
}
private static class CustomOrderBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, spec -> spec.order(1));
}
}
private static class PrimaryBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::primary);
}
}
private static class PrototypeBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, BeanRegistry.Spec::prototype);
}
}
private static class SupplierBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, spec -> spec.supplier(context -> new Foo()));
}
}
private static class Foo {}
}