mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Polish BeanRegistrar Javadoc and add tests for non-invocation semantics
Revise the BeanRegistrar Javadoc to document the two distinct usage modes: @Configuration/@Import and programmatic GenericApplicationContext setup. Clarify that implementations are not Spring components (requiring a no-arg constructor and no dependency injection), and detail the ordering guarantees for each mode. Add missing tests Signed-off-by: Stéphane Nicoll <stephane.nicoll@broadcom.com>
This commit is contained in:
committed by
Juergen Hoeller
parent
5dc4a3a7a0
commit
d5b6f4a7ee
@@ -19,21 +19,9 @@ package org.springframework.beans.factory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Contract for registering beans programmatically, typically imported with an
|
||||
* {@link org.springframework.context.annotation.Import @Import} annotation on
|
||||
* a {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* class.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @Import(MyBeanRegistrar.class)
|
||||
* class MyConfiguration {
|
||||
* }</pre>
|
||||
* Can also be applied to an application context via
|
||||
* {@link org.springframework.context.support.GenericApplicationContext#register(BeanRegistrar...)}.
|
||||
* Contract for registering beans programmatically. Implementations use the
|
||||
* {@link BeanRegistry} and {@link Environment} to register beans:
|
||||
*
|
||||
*
|
||||
* <p>Bean registrar implementations use {@link BeanRegistry} and {@link Environment}
|
||||
* APIs to register beans programmatically in a concise and flexible way.
|
||||
* <pre class="code">
|
||||
* class MyBeanRegistrar implements BeanRegistrar {
|
||||
*
|
||||
@@ -52,9 +40,55 @@ import org.springframework.core.env.Environment;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>{@code BeanRegistrar} implementations are not Spring components: they must have
|
||||
* a no-arg constructor and cannot rely on dependency injection or any other
|
||||
* component-model feature. They can be used in two distinct ways depending on the
|
||||
* application context setup.
|
||||
*
|
||||
* <h3>With the {@code @Configuration} model</h3>
|
||||
*
|
||||
* <p>A {@code BeanRegistrar} must be imported via
|
||||
* {@link org.springframework.context.annotation.Import @Import} on a
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} class:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @Import(MyBeanRegistrar.class)
|
||||
* class MyConfiguration {
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This is the only mechanism that triggers bean registration in the annotation-based
|
||||
* configuration model. Annotating an implementation with {@code @Configuration} or
|
||||
* {@code @Component}, or returning an instance from a {@code @Bean} method, registers
|
||||
* it as a bean but does <strong>not</strong> invoke its
|
||||
* {@link #register(BeanRegistry, Environment) register} method.
|
||||
*
|
||||
* <p>When imported, the registrar is invoked in the order it is encountered during
|
||||
* configuration class processing. It can therefore check for and build on beans that
|
||||
* have already been defined, but has no visibility into beans that will be registered
|
||||
* by classes processed later.
|
||||
*
|
||||
* <h3>Programmatic usage</h3>
|
||||
*
|
||||
* <p>A {@code BeanRegistrar} can also be applied directly to a
|
||||
* {@link org.springframework.context.support.GenericApplicationContext}:
|
||||
*
|
||||
* <pre class="code">
|
||||
* GenericApplicationContext context = new GenericApplicationContext();
|
||||
* context.register(new MyBeanRegistrar());
|
||||
* context.registerBean("myBean", MyBean.class);
|
||||
* context.refresh();</pre>
|
||||
*
|
||||
* <p>This mode is primarily intended for fully programmatic application context setups.
|
||||
* Registrars applied this way are invoked before any {@code @Configuration} class is
|
||||
* processed. They can therefore observe beans registered programmatically (e.g., via
|
||||
* {@link org.springframework.context.support.GenericApplicationContext#registerBean(Class)}),
|
||||
* but will <strong>not</strong> see any beans defined in {@code @Configuration} classes
|
||||
* also registered with the context.
|
||||
*
|
||||
* <p>A {@code BeanRegistrar} implementing {@link org.springframework.context.annotation.ImportAware}
|
||||
* can optionally introspect import metadata when used in an import scenario, otherwise the
|
||||
* {@code setImportMetadata} method is simply not being called.
|
||||
* can optionally introspect import metadata when used in an import scenario; otherwise
|
||||
* the {@code setImportMetadata} method is not called.
|
||||
*
|
||||
* <p>In Kotlin, it is recommended to use {@code BeanRegistrarDsl} instead of
|
||||
* implementing {@code BeanRegistrar}.
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* <p>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 {
|
||||
|
||||
}
|
||||
+42
-3
@@ -34,19 +34,28 @@ import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar
|
||||
import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar.Foo;
|
||||
import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar.Init;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.BeanRegistrarConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ComponentBeanRegistrar;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ComponentBeanRegistrar.IgnoredFromComponent;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ConditionalBeanRegistrarConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ConfigurationBeanRegistrar;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ConfigurationBeanRegistrar.BeanBeanRegistrar;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ConfigurationBeanRegistrar.IgnoredFromBean;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ConfigurationBeanRegistrar.IgnoredFromConfiguration;
|
||||
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.TestBeanConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanRegistrar} imported by @{@link org.springframework.context.annotation.Configuration}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanRegistrarConfigurationTests {
|
||||
|
||||
@@ -63,6 +72,36 @@ class BeanRegistrarConfigurationTests {
|
||||
assertThat(beanDefinition.getDescription()).isEqualTo("Custom description");
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanRegistrarIgnoreBeans() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationBeanRegistrar.class);
|
||||
assertThatNoException().isThrownBy(() -> context.getBean(ConfigurationBeanRegistrar.class));
|
||||
assertThatNoException().isThrownBy(() -> context.getBean(BeanBeanRegistrar.class));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBean(IgnoredFromConfiguration.class));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBean(IgnoredFromBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanRegistrarWithClasspathScanningIgnoreBeans() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.scan("org.springframework.context.testfixture.context.annotation.registrar");
|
||||
context.refresh();
|
||||
|
||||
assertThatNoException().isThrownBy(() -> context.getBean(ConfigurationBeanRegistrar.class));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBean(IgnoredFromConfiguration.class));
|
||||
|
||||
assertThatNoException().isThrownBy(() -> context.getBean(BeanBeanRegistrar.class));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBean(IgnoredFromBean.class));
|
||||
|
||||
assertThatNoException().isThrownBy(() -> context.getBean(ComponentBeanRegistrar.class));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBean(IgnoredFromComponent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanRegistrarWithProfile() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
@@ -110,16 +149,16 @@ class BeanRegistrarConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void programmaticBeanRegistrarWithConditionNotMet() {
|
||||
void programmaticBeanRegistrarIsInvokedBeforeConfigurationClassPostProcessor() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(new ConditionalBeanRegistrar());
|
||||
context.register(TestBeanConfiguration.class);
|
||||
context.register(new ConditionalBeanRegistrar());
|
||||
context.refresh();
|
||||
assertThat(context.containsBean("myTestBean")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void programmaticBeanRegistrarWithConditionMet() {
|
||||
void programmaticBeanRegistrarHandlesProgrammaticRegisteredBean() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(new ConditionalBeanRegistrar());
|
||||
context.registerBean("testBean", TestBean.class);
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.factory.BeanRegistrar;
|
||||
import org.springframework.beans.factory.BeanRegistry;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ComponentBeanRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(IgnoredFromComponent.class);
|
||||
}
|
||||
|
||||
|
||||
public record IgnoredFromComponent() {}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.factory.BeanRegistrar;
|
||||
import org.springframework.beans.factory.BeanRegistry;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@Configuration
|
||||
public class ConfigurationBeanRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(IgnoredFromConfiguration.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanBeanRegistrar beanBeanRegistrar() {
|
||||
return new BeanBeanRegistrar();
|
||||
}
|
||||
|
||||
public static class BeanBeanRegistrar implements BeanRegistrar {
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(IgnoredFromBean.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public record IgnoredFromConfiguration() {}
|
||||
|
||||
public record IgnoredFromBean() {}
|
||||
}
|
||||
Reference in New Issue
Block a user