From 7502b923922d02109b5a3c861033715f973dc3cd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 28 Mar 2026 20:27:23 +0100 Subject: [PATCH] Introduce DeferredBeanRegistrar and BeanRegistry#containsBean methods Closes gh-21497 --- .../beans/factory/BeanRegistry.java | 23 +++++++++ .../factory/support/BeanRegistryAdapter.java | 32 +++++++++--- .../context/DeferredBeanRegistrar.java | 46 +++++++++++++++++ ...onfigurationClassBeanDefinitionReader.java | 17 ++++--- .../ConfigurationClassPostProcessor.java | 25 +++++++++- .../context/annotation/Import.java | 12 +++-- .../support/GenericApplicationContext.java | 49 ++++++++++++++++++- .../BeanRegistrarConfigurationTests.java | 41 ++++++++++++++++ .../GenericApplicationContextTests.java | 40 +++++++++++++++ .../factory/MyDeferredBeanRegistrar.java | 37 ++++++++++++++ .../beans/factory/MyRegularBeanRegistrar.java | 36 ++++++++++++++ .../MyDeferredBeanRegistrarConfiguration.java | 26 ++++++++++ .../MyRegularBeanRegistrarConfiguration.java | 26 ++++++++++ .../registrar/TestBeanConfiguration.java | 31 ++++++++++++ 14 files changed, 420 insertions(+), 21 deletions(-) create mode 100644 spring-context/src/main/java/org/springframework/context/DeferredBeanRegistrar.java create mode 100644 spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyDeferredBeanRegistrar.java create mode 100644 spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyRegularBeanRegistrar.java create mode 100644 spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyDeferredBeanRegistrarConfiguration.java create mode 100644 spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyRegularBeanRegistrarConfiguration.java create mode 100644 spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/TestBeanConfiguration.java 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 07e1eb5da07..cadc878644d 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 @@ -33,6 +33,7 @@ import org.springframework.core.env.Environment; * programmatic bean registration capabilities. * * @author Sebastien Deleuze + * @author Juergen Hoeller * @since 7.0 */ public interface BeanRegistry { @@ -140,6 +141,28 @@ public interface BeanRegistry { */ void registerBean(String name, ParameterizedTypeReference beanType, Consumer> customizer); + /** + * Determine whether a bean of the given name is already registered. + * @param name the name of the bean + * @since 7.1 + */ + boolean containsBean(String name); + + /** + * Determine whether a bean of the given type is already registered. + * @param beanType the type of the bean + * @since 7.1 + */ + boolean containsBean(Class beanType); + + /** + * Determine whether a bean of the given generics-containing type is + * already registered. + * @param beanType the generics-containing type of the bean + * @since 7.1 + */ + boolean containsBean(ParameterizedTypeReference beanType); + /** * Specification for customizing a bean. 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 47b835c5353..cfc3135ce37 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 @@ -25,6 +25,8 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanRegistrar; import org.springframework.beans.factory.BeanRegistry; import org.springframework.beans.factory.ListableBeanFactory; @@ -81,6 +83,12 @@ public class BeanRegistryAdapter implements BeanRegistry { } + @Override + public void register(BeanRegistrar registrar) { + Assert.notNull(registrar, "BeanRegistrar must not be null"); + registrar.register(this, this.environment); + } + @Override public void registerAlias(String name, String alias) { this.beanRegistry.registerAlias(name, alias); @@ -170,9 +178,19 @@ public class BeanRegistryAdapter implements BeanRegistry { } @Override - public void register(BeanRegistrar registrar) { - Assert.notNull(registrar, "'registrar' must not be null"); - registrar.register(this, this.environment); + public boolean containsBean(String name) { + return this.beanFactory.containsBean(name); + } + + @Override + public boolean containsBean(Class beanType) { + return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, beanType).length > 0; + } + + @Override + public boolean containsBean(ParameterizedTypeReference beanType) { + ResolvableType resolvableType = ResolvableType.forType(beanType); + return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, resolvableType).length > 0; } @@ -218,9 +236,9 @@ public class BeanRegistryAdapter implements BeanRegistry { private final RootBeanDefinition beanDefinition; - private final ListableBeanFactory beanFactory; + private final BeanFactory beanFactory; - public BeanSpecAdapter(RootBeanDefinition beanDefinition, ListableBeanFactory beanFactory) { + public BeanSpecAdapter(RootBeanDefinition beanDefinition, BeanFactory beanFactory) { this.beanDefinition = beanDefinition; this.beanFactory = beanFactory; } @@ -296,9 +314,9 @@ public class BeanRegistryAdapter implements BeanRegistry { private static class SupplierContextAdapter implements SupplierContext { - private final ListableBeanFactory beanFactory; + private final BeanFactory beanFactory; - public SupplierContextAdapter(ListableBeanFactory beanFactory) { + public SupplierContextAdapter(BeanFactory beanFactory) { this.beanFactory = beanFactory; } diff --git a/spring-context/src/main/java/org/springframework/context/DeferredBeanRegistrar.java b/spring-context/src/main/java/org/springframework/context/DeferredBeanRegistrar.java new file mode 100644 index 00000000000..77ed04f7383 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/context/DeferredBeanRegistrar.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-present 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.context; + +import org.springframework.beans.factory.BeanRegistrar; +import org.springframework.beans.factory.BeanRegistry; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; + +/** + * A variant of {@link BeanRegistrar} which aims to be invoked + * at the end of the bean registration phase, coming after regular + * bean definition reading and configuration class processing. + * + *

This allows for seeing all user-registered beans, potentially + * reacting to their presence. The {@code containsBean} methods on + * {@link BeanRegistry} will provide reliable answers, independent + * of the order of user bean registration versus {@code BeanRegistrar} + * import/registration. + * + * @author Juergen Hoeller + * @since 7.1 + * @see #register(BeanRegistry, Environment) + * @see BeanRegistry#containsBean(String) + * @see BeanRegistry#containsBean(Class) + * @see BeanRegistry#containsBean(ParameterizedTypeReference) + * @see org.springframework.context.support.GenericApplicationContext#register( BeanRegistrar...) + * @see org.springframework.context.annotation.Import + */ +public interface DeferredBeanRegistrar extends BeanRegistrar { + +} diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 56ceb8f5ab5..86b9aeee520 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -45,6 +45,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.BeanRegistryAdapter; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.context.DeferredBeanRegistrar; import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; @@ -423,12 +424,16 @@ class ConfigurationClassBeanDefinitionReader { } private void loadBeanDefinitionsFromBeanRegistrars(MultiValueMap registrars) { - if (!(this.registry instanceof ListableBeanFactory beanFactory)) { - throw new IllegalStateException("Cannot support bean registrars since " + - this.registry.getClass().getName() + " does not implement ListableBeanFactory"); - } - registrars.values().forEach(registrarList -> registrarList.forEach(registrar -> registrar.register(new BeanRegistryAdapter( - this.registry, beanFactory, this.environment, registrar.getClass()), this.environment))); + registrars.values().forEach(registrarList -> registrarList.forEach(registrar -> { + if (!(registrar instanceof DeferredBeanRegistrar)) { + if (!(this.registry instanceof ListableBeanFactory beanFactory)) { + throw new IllegalStateException("Cannot support bean registrars since " + + this.registry.getClass().getName() + " does not implement ListableBeanFactory"); + } + registrar.register(new BeanRegistryAdapter( + this.registry, beanFactory, this.environment, registrar.getClass()), this.environment); + } + })); } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 91e383489ed..033b5a5ccac 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -94,11 +94,13 @@ import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationStartupAware; +import org.springframework.context.DeferredBeanRegistrar; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.StandardEnvironment; @@ -208,7 +210,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo @Override public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; // within PriorityOrdered + return Ordered.LOWEST_PRECEDENCE - 1; // within PriorityOrdered, 1 before DeferredRegistryPostProcessor } /** @@ -222,7 +224,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo /** * Set the {@link ProblemReporter} to use. *

Used to register any problems detected with {@link Configuration} or {@link Bean} - * declarations. For instance, an @Bean method marked as {@code final} is illegal + * declarations. For instance, a @Bean method marked as {@code final} is illegal * and would be reported as a problem. Defaults to {@link FailFastProblemReporter}. */ public void setProblemReporter(@Nullable ProblemReporter problemReporter) { @@ -490,6 +492,25 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo } while (!candidates.isEmpty()); + // Process DeferredBeanRegistrars, if any. + List deferredBeanRegistrars = new ArrayList<>(); + for (List registrars : this.beanRegistrars.values()) { + for (BeanRegistrar registrar : registrars) { + if (registrar instanceof DeferredBeanRegistrar deferredRegistrar) { + deferredBeanRegistrars.add(deferredRegistrar); + } + } + } + AnnotationAwareOrderComparator.sort(deferredBeanRegistrars); + for (DeferredBeanRegistrar registrar : deferredBeanRegistrars) { + if (!(registry instanceof ListableBeanFactory beanFactory)) { + throw new IllegalStateException("Cannot support bean registrars since " + + registry.getClass().getName() + " does not implement ListableBeanFactory"); + } + registrar.register(new BeanRegistryAdapter( + registry, beanFactory, this.environment, registrar.getClass()), this.environment); + } + // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (singletonRegistry != null && !singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Import.java b/spring-context/src/main/java/org/springframework/context/annotation/Import.java index 04fc8db01bf..50639667ded 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Import.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Import.java @@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.BeanRegistrar; +import org.springframework.context.DeferredBeanRegistrar; /** * Indicates one or more component classes to import — typically @@ -31,9 +32,9 @@ import org.springframework.beans.factory.BeanRegistrar; *

Provides functionality equivalent to the {@code } element in Spring XML. * *

Allows for importing {@code @Configuration} classes, {@link ImportSelector}, - * {@link ImportBeanDefinitionRegistrar}, and {@link BeanRegistrar} implementations, - * as well as regular component classes (analogous to - * {@link AnnotationConfigApplicationContext#register}). + * {@link ImportBeanDefinitionRegistrar}, and {@link BeanRegistrar}/{@link DeferredBeanRegistrar} + * implementations, as well as regular component classes + * (analogous to {@link AnnotationConfigApplicationContext#register}). * *

{@code @Bean} definitions declared in imported {@code @Configuration} classes should be * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired} @@ -71,8 +72,9 @@ public @interface Import { /** * {@link Configuration @Configuration}, {@link ImportSelector}, - * {@link ImportBeanDefinitionRegistrar}, {@link BeanRegistrar}, or regular - * component classes to import. + * {@link ImportBeanDefinitionRegistrar}, + * {@link BeanRegistrar}/{@link DeferredBeanRegistrar}, + * or regular component classes to import. */ Class[] value(); diff --git a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java index 9a75057a7f2..21fedb98759 100644 --- a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java @@ -44,6 +44,10 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; +import org.springframework.context.DeferredBeanRegistrar; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.io.ProtocolResolver; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -105,6 +109,10 @@ import org.springframework.util.Assert; */ public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { + private static final String DEFERRED_REGISTRY_POST_PROCESSOR_BEAN_NAME = + GenericApplicationContext.class.getName() + ".deferredRegistryPostProcessor"; + + private final DefaultListableBeanFactory beanFactory; private @Nullable ResourceLoader resourceLoader; @@ -604,7 +612,18 @@ public class GenericApplicationContext extends AbstractApplicationContext implem */ public void register(BeanRegistrar... registrars) { for (BeanRegistrar registrar : registrars) { - new BeanRegistryAdapter(this.beanFactory, getEnvironment(), registrar.getClass()).register(registrar); + if (registrar instanceof DeferredBeanRegistrar deferredRegistrar) { + DeferredRegistryPostProcessor pp = (DeferredRegistryPostProcessor) + this.beanFactory.getSingleton(DEFERRED_REGISTRY_POST_PROCESSOR_BEAN_NAME); + if (pp == null) { + pp = new DeferredRegistryPostProcessor(); + this.beanFactory.registerSingleton(DEFERRED_REGISTRY_POST_PROCESSOR_BEAN_NAME, pp); + } + pp.addRegistrar(deferredRegistrar); + } + else { + new BeanRegistryAdapter(this.beanFactory, getEnvironment(), registrar.getClass()).register(registrar); + } } } @@ -648,4 +667,32 @@ public class GenericApplicationContext extends AbstractApplicationContext implem } } + + /** + * Internal post-processor for invoking DeferredBeanRegistrars at the end + * of the BeanDefinitionRegistryPostProcessor PriorityOrdered phase, + * right after a potential ConfigurationClassPostProcessor. + */ + private class DeferredRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered { + + private final List registrars = new ArrayList<>(); + + public void addRegistrar(DeferredBeanRegistrar registrar) { + this.registrars.add(registrar); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; // within PriorityOrdered, 1 behind ConfigurationClassPostProcessor + } + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + AnnotationAwareOrderComparator.sort(this.registrars); + for (DeferredBeanRegistrar registrar : this.registrars) { + new BeanRegistryAdapter(beanFactory, getEnvironment(), registrar.getClass()).register(registrar); + } + } + } + } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/beanregistrar/BeanRegistrarConfigurationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/beanregistrar/BeanRegistrarConfigurationTests.java index 04f40c8c002..ac67677b24e 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/beanregistrar/BeanRegistrarConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/beanregistrar/BeanRegistrarConfigurationTests.java @@ -22,6 +22,7 @@ import org.springframework.beans.factory.BeanRegistrar; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.testfixture.beans.factory.BarRegistrar; import org.springframework.context.testfixture.beans.factory.FooRegistrar; @@ -35,6 +36,9 @@ import org.springframework.context.testfixture.context.annotation.registrar.Bean import org.springframework.context.testfixture.context.annotation.registrar.GenericBeanRegistrarConfiguration; import org.springframework.context.testfixture.context.annotation.registrar.ImportAwareBeanRegistrarConfiguration; import org.springframework.context.testfixture.context.annotation.registrar.MultipleBeanRegistrarsConfiguration; +import org.springframework.context.testfixture.context.annotation.registrar.MyDeferredBeanRegistrarConfiguration; +import org.springframework.context.testfixture.context.annotation.registrar.MyRegularBeanRegistrarConfiguration; +import org.springframework.context.testfixture.context.annotation.registrar.TestBeanConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -105,4 +109,41 @@ class BeanRegistrarConfigurationTests { assertThat(context.getBean(BarRegistrar.Bar.class)).isNotNull(); } + @Test + void regularBeanRegistrarWithConditionMet() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(TestBeanConfiguration.class); + context.register(MyRegularBeanRegistrarConfiguration.class); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isTrue(); + assertThat(context.getBean("myTestBean")).isInstanceOf(TestBean.class); + } + + @Test + void regularBeanRegistrarWithConditionNotMet() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(MyRegularBeanRegistrarConfiguration.class); + context.register(TestBeanConfiguration.class); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isFalse(); + } + + @Test + void deferredBeanRegistrarWithConditionMet() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(MyDeferredBeanRegistrarConfiguration.class); + context.register(TestBeanConfiguration.class); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isTrue(); + assertThat(context.getBean("myTestBean")).isInstanceOf(TestBean.class); + } + + @Test + void deferredBeanRegistrarWithConditionNotMet() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(MyDeferredBeanRegistrarConfiguration.class); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isFalse(); + } + } diff --git a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java index 0694ad4462f..a5818cff229 100644 --- a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java @@ -44,9 +44,12 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.testfixture.beans.factory.ImportAwareBeanRegistrar; +import org.springframework.context.testfixture.beans.factory.MyDeferredBeanRegistrar; +import org.springframework.context.testfixture.beans.factory.MyRegularBeanRegistrar; import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar; import org.springframework.core.DecoratingProxy; import org.springframework.core.env.ConfigurableEnvironment; @@ -645,6 +648,43 @@ class GenericApplicationContextTests { assertThat(context.getBean(ImportAwareBeanRegistrar.ClassNameHolder.class).className()).isNull(); } + @Test + void regularBeanRegistrarWithConditionMet() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("testBean", TestBean.class); + context.register(new MyRegularBeanRegistrar()); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isTrue(); + assertThat(context.getBean("myTestBean")).isInstanceOf(TestBean.class); + } + + @Test + void regularBeanRegistrarWithConditionNotMet() { + GenericApplicationContext context = new GenericApplicationContext(); + context.register(new MyRegularBeanRegistrar()); + context.registerBean("testBean", TestBean.class); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isFalse(); + } + + @Test + void deferredBeanRegistrarWithConditionMet() { + GenericApplicationContext context = new GenericApplicationContext(); + context.register(new MyDeferredBeanRegistrar()); + context.registerBean("testBean", TestBean.class); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isTrue(); + assertThat(context.getBean("myTestBean")).isInstanceOf(TestBean.class); + } + + @Test + void deferredBeanRegistrarWithConditionNotMet() { + GenericApplicationContext context = new GenericApplicationContext(); + context.register(new MyDeferredBeanRegistrar()); + context.refresh(); + assertThat(context.containsBean("myTestBean")).isFalse(); + } + private MergedBeanDefinitionPostProcessor registerMockMergedBeanDefinitionPostProcessor(GenericApplicationContext context) { MergedBeanDefinitionPostProcessor bpp = mock(); diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyDeferredBeanRegistrar.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyDeferredBeanRegistrar.java new file mode 100644 index 00000000000..d4f85b8a19d --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyDeferredBeanRegistrar.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-present 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.context.testfixture.beans.factory; + +import org.springframework.beans.factory.BeanRegistry; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.context.DeferredBeanRegistrar; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; + +public class MyDeferredBeanRegistrar implements DeferredBeanRegistrar { + + @Override + public void register(BeanRegistry registry, Environment env) { + if (registry.containsBean("testBean") && + registry.containsBean(TestBean.class) && + registry.containsBean(new ParameterizedTypeReference>() { + })) { + registry.registerBean("myTestBean", TestBean.class); + } + } + +} diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyRegularBeanRegistrar.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyRegularBeanRegistrar.java new file mode 100644 index 00000000000..14e2637ce97 --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/MyRegularBeanRegistrar.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-present 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.context.testfixture.beans.factory; + +import org.springframework.beans.factory.BeanRegistrar; +import org.springframework.beans.factory.BeanRegistry; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; + +public class MyRegularBeanRegistrar implements BeanRegistrar { + + @Override + public void register(BeanRegistry registry, Environment env) { + if (registry.containsBean("testBean") && + registry.containsBean(TestBean.class) && + registry.containsBean(new ParameterizedTypeReference>() { + })) { + registry.registerBean("myTestBean", TestBean.class); + } + } +} diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyDeferredBeanRegistrarConfiguration.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyDeferredBeanRegistrarConfiguration.java new file mode 100644 index 00000000000..1f3ddbd0d2c --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyDeferredBeanRegistrarConfiguration.java @@ -0,0 +1,26 @@ +/* + * Copyright 2002-present 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.context.testfixture.context.annotation.registrar; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.testfixture.beans.factory.MyDeferredBeanRegistrar; + +@Configuration +@Import(MyDeferredBeanRegistrar.class) +public class MyDeferredBeanRegistrarConfiguration { +} diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyRegularBeanRegistrarConfiguration.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyRegularBeanRegistrarConfiguration.java new file mode 100644 index 00000000000..2176e7f6f5f --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/MyRegularBeanRegistrarConfiguration.java @@ -0,0 +1,26 @@ +/* + * Copyright 2002-present 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.context.testfixture.context.annotation.registrar; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.testfixture.beans.factory.MyRegularBeanRegistrar; + +@Configuration +@Import(MyRegularBeanRegistrar.class) +public class MyRegularBeanRegistrarConfiguration { +} diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/TestBeanConfiguration.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/TestBeanConfiguration.java new file mode 100644 index 00000000000..c786f7f4fea --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/registrar/TestBeanConfiguration.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-present 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.context.testfixture.context.annotation.registrar; + +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestBeanConfiguration { + + @Bean + public TestBean testBean() { + return new TestBean(); + } + +}