Allow `ApplicationContextRunner` to accept simple bean definitions

This commit adds `withBean` methods to the `ApplicationContextRunner`
abstraction so that simple beans can be registered inline. This is a
nice alternative for cases where a inner configuration class has to be
defined for the purpose of creating a simple bean.

Closes gh-16011
pull/16513/head
Stephane Nicoll 6 years ago
parent 7054a33e70
commit a780875390

@ -22,6 +22,7 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.context.annotation.Configurations; import org.springframework.boot.context.annotation.Configurations;
import org.springframework.boot.context.annotation.UserConfigurations; import org.springframework.boot.context.annotation.UserConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.FilteredClassLoader;
@ -32,6 +33,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigRegistry; import org.springframework.context.annotation.AnnotationConfigRegistry;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
@ -156,8 +158,7 @@ public abstract class AbstractApplicationContextRunner<SELF extends AbstractAppl
* @param initializer the initializer to add * @param initializer the initializer to add
* @return a new instance with the updated initializers * @return a new instance with the updated initializers
*/ */
public SELF withInitializer( public SELF withInitializer(ApplicationContextInitializer<? super C> initializer) {
ApplicationContextInitializer<? super ConfigurableApplicationContext> initializer) {
Assert.notNull(initializer, "Initializer must not be null"); Assert.notNull(initializer, "Initializer must not be null");
return newInstance(this.contextFactory, add(this.initializers, initializer), return newInstance(this.contextFactory, add(this.initializers, initializer),
this.environmentProperties, this.systemProperties, this.classLoader, this.environmentProperties, this.systemProperties, this.classLoader,
@ -221,6 +222,84 @@ public abstract class AbstractApplicationContextRunner<SELF extends AbstractAppl
parent, this.configurations); parent, this.configurations);
} }
/**
* Register the specified user bean with the {@link ApplicationContext}. The bean name
* is generated from the configured {@link BeanNameGenerator} on the underlying
* context.
* <p>
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
* user configurations} in the order of registration.
* @param beanType the type of the bean
* @param beanDefinition a supplier for the bean
* @param <T> the type of the bean
* @return a new instance with the updated bean
*/
public <T> SELF withBean(Class<T> beanType, Supplier<T> beanDefinition) {
return withBean(null, beanType, beanDefinition);
}
/**
* Register the specified user bean with the {@link ApplicationContext}. The bean name
* is generated from the configured {@link BeanNameGenerator} on the underlying
* context.
* <p>
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
* user configurations} in the order of registration.
* @param beanType the type of the bean
* @param beanDefinition a function that accepts the context and return the bean
* @param <T> the type of the bean
* @return a new instance with the updated bean
*/
public <T> SELF withBean(Class<T> beanType, Function<? super C, T> beanDefinition) {
return withBean(null, beanType, beanDefinition);
}
/**
* Register the specified user bean with the {@link ApplicationContext}. If no bean
* name is provided, a default one is generated from the configured
* {@link BeanNameGenerator} on the underlying context.
* <p>
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
* user configurations} in the order of registration.
* @param beanName the name of the bean (may be {@code null})
* @param beanType the type of the bean
* @param beanDefinition a supplier for the bean
* @param <T> the type of the bean
* @return a new instance with the updated bean
*/
public <T> SELF withBean(String beanName, Class<T> beanType,
Supplier<T> beanDefinition) {
return withBean(beanName, beanType, (context) -> beanDefinition.get());
}
/**
* Register the specified user bean with the {@link ApplicationContext}. If no bean
* name is provided, a default one is generated from the configured
* {@link BeanNameGenerator} on the underlying context.
* <p>
* Such beans are registered after regular {@linkplain #withUserConfiguration(Class[])
* user configurations} in the order of registration.
* @param beanName the name of the bean (may be {@code null})
* @param beanType the type of the bean
* @param beanDefinition a function that accepts the context and return the bean
* @param <T> the type of the bean
* @return a new instance with the updated bean
*/
public <T> SELF withBean(String beanName, Class<T> beanType,
Function<? super C, T> beanDefinition) {
return withInitializer(
beanDefinitionRegistrar(beanName, beanType, beanDefinition));
}
private <T> ApplicationContextInitializer<? super C> beanDefinitionRegistrar(
String beanName, Class<T> beanType, Function<? super C, T> beanDefinition) {
return (context) -> {
Assert.isInstanceOf(GenericApplicationContext.class, context);
((GenericApplicationContext) context).registerBean(beanName, beanType,
() -> beanDefinition.apply(context));
};
}
/** /**
* Register the specified user configuration classes with the * Register the specified user configuration classes with the
* {@link ApplicationContext}. * {@link ApplicationContext}.

@ -39,6 +39,7 @@ import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException; import static org.assertj.core.api.Assertions.assertThatIOException;
import static org.assertj.core.api.Assertions.entry;
/** /**
* Abstract tests for {@link AbstractApplicationContextRunner} implementations. * Abstract tests for {@link AbstractApplicationContextRunner} implementations.
@ -136,6 +137,45 @@ public abstract class AbstractApplicationContextRunnerTests<T extends AbstractAp
.run((context) -> assertThat(context).hasBean("foo")); .run((context) -> assertThat(context).hasBean("foo"));
} }
@Test
public void runWithUserNamedBeanShouldRegisterBean() {
get().withBean("foo", String.class, () -> "foo")
.run((context) -> assertThat(context).hasBean("foo"));
}
@Test
public void runWithUserBeanShouldRegisterBeanWithDefaultName() {
get().withBean(String.class, () -> "foo")
.run((context) -> assertThat(context).hasBean("string"));
}
@Test
public void runWithUserBeanShouldBeRegisteredInOrder() {
get().withBean(String.class, () -> "one").withBean(String.class, () -> "two")
.withBean(String.class, () -> "three").run((context) -> {
assertThat(context).hasBean("string");
assertThat(context.getBean("string")).isEqualTo("three");
});
}
@Test
public void runWithConfigurationsAndUserBeanShouldRegisterUserBeanLast() {
get().withUserConfiguration(FooConfig.class)
.withBean("foo", String.class, () -> "overridden").run((context) -> {
assertThat(context).hasBean("foo");
assertThat(context.getBean("foo")).isEqualTo("overridden");
});
}
@Test
public void runWithUserBeanShouldHaveAccessToContext() {
get().withUserConfiguration(FooConfig.class)
.withBean(String.class, (context) -> "Result: " + context.getBean("foo"))
.run((context) -> assertThat(context.getBeansOfType(String.class))
.containsOnly(entry("foo", "foo"),
entry("string", "Result: foo")));
}
@Test @Test
public void runWithMultipleConfigurationsShouldRegisterAllConfigurations() { public void runWithMultipleConfigurationsShouldRegisterAllConfigurations() {
get().withUserConfiguration(FooConfig.class) get().withUserConfiguration(FooConfig.class)

Loading…
Cancel
Save