Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,9 @@
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">
* &#064;Configuration
* &#064;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 {
*
Expand All @@ -52,9 +40,55 @@
* }
* }</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">
* &#064;Configuration
* &#064;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}.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,28 @@
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 {

Expand All @@ -63,6 +72,36 @@ void beanRegistrar() {
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();
Expand Down Expand Up @@ -110,16 +149,16 @@ void multipleBeanRegistrars() {
}

@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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {}

}
Original file line number Diff line number Diff line change
@@ -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() {}
}