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:
@@ -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 {}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user