Merge pull request #29500 from VladislavSevruk

* pr/29500:
  Polish @ConditionalOnBean and @ConditionalOnMissingBean tests
  Add ConditionalOnBean support for generic @Bean return types

Closes gh-29500
This commit is contained in:
Phillip Webb
2025-01-28 20:14:37 -08:00
3 changed files with 593 additions and 280 deletions

View File

@@ -76,6 +76,7 @@ import org.springframework.util.StringUtils;
* @author Jakub Kubrynski
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Uladzislau Seuruk
* @see ConditionalOnBean
* @see ConditionalOnMissingBean
* @see ConditionalOnSingleCandidate
@@ -205,13 +206,13 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
ConfigurableListableBeanFactory beanFactory = getSearchBeanFactory(spec);
ClassLoader classLoader = spec.getContext().getClassLoader();
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
Set<ResolvableType> parameterizedContainers = spec.getParameterizedContainers();
MatchResult result = new MatchResult();
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(beanFactory, considerHierarchy,
spec.getIgnoredTypes(), parameterizedContainers);
for (String type : spec.getTypes()) {
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(classLoader,
considerHierarchy, beanFactory, type, parameterizedContainers);
for (ResolvableType type : spec.getTypes()) {
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(beanFactory,
considerHierarchy, type, parameterizedContainers);
Set<String> typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions,
(name, definition) -> !ScopedProxyUtils.isScopedTarget(name)
&& isCandidate(beanFactory, name, definition, beansIgnoredByType));
@@ -296,42 +297,31 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return true;
}
private Set<String> getNamesOfBeansIgnoredByType(ClassLoader classLoader, ListableBeanFactory beanFactory,
boolean considerHierarchy, Set<String> ignoredTypes, Set<Class<?>> parameterizedContainers) {
private Set<String> getNamesOfBeansIgnoredByType(ListableBeanFactory beanFactory, boolean considerHierarchy,
Set<ResolvableType> ignoredTypes, Set<ResolvableType> parameterizedContainers) {
Set<String> result = null;
for (String ignoredType : ignoredTypes) {
Collection<String> ignoredNames = getBeanDefinitionsForType(classLoader, considerHierarchy, beanFactory,
ignoredType, parameterizedContainers)
for (ResolvableType ignoredType : ignoredTypes) {
Collection<String> ignoredNames = getBeanDefinitionsForType(beanFactory, considerHierarchy, ignoredType,
parameterizedContainers)
.keySet();
result = addAll(result, ignoredNames);
}
return (result != null) ? result : Collections.emptySet();
}
private Map<String, BeanDefinition> getBeanDefinitionsForType(ClassLoader classLoader, boolean considerHierarchy,
ListableBeanFactory beanFactory, String type, Set<Class<?>> parameterizedContainers) throws LinkageError {
try {
return getBeanDefinitionsForType(beanFactory, considerHierarchy, resolve(type, classLoader),
parameterizedContainers);
}
catch (ClassNotFoundException | NoClassDefFoundError ex) {
return Collections.emptyMap();
}
}
private Map<String, BeanDefinition> getBeanDefinitionsForType(ListableBeanFactory beanFactory,
boolean considerHierarchy, Class<?> type, Set<Class<?>> parameterizedContainers) {
boolean considerHierarchy, ResolvableType type, Set<ResolvableType> parameterizedContainers) {
Map<String, BeanDefinition> result = collectBeanDefinitionsForType(beanFactory, considerHierarchy, type,
parameterizedContainers, null);
return (result != null) ? result : Collections.emptyMap();
}
private Map<String, BeanDefinition> collectBeanDefinitionsForType(ListableBeanFactory beanFactory,
boolean considerHierarchy, Class<?> type, Set<Class<?>> parameterizedContainers,
boolean considerHierarchy, ResolvableType type, Set<ResolvableType> parameterizedContainers,
Map<String, BeanDefinition> result) {
result = putAll(result, beanFactory.getBeanNamesForType(type, true, false), beanFactory);
for (Class<?> container : parameterizedContainers) {
ResolvableType generic = ResolvableType.forClassWithGenerics(container, type);
for (ResolvableType parameterizedContainer : parameterizedContainers) {
ResolvableType generic = ResolvableType.forClassWithGenerics(parameterizedContainer.resolve(), type);
result = putAll(result, beanFactory.getBeanNamesForType(generic, true, false), beanFactory);
}
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory hierarchicalBeanFactory) {
@@ -550,13 +540,13 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
private final Set<String> names;
private final Set<String> types;
private final Set<ResolvableType> types;
private final Set<String> annotations;
private final Set<String> ignoredTypes;
private final Set<ResolvableType> ignoredTypes;
private final Set<Class<?>> parameterizedContainers;
private final Set<ResolvableType> parameterizedContainers;
private final SearchStrategy strategy;
@@ -570,10 +560,10 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
this.annotationType = annotationType;
this.names = extract(attributes, "name");
this.annotations = extract(attributes, "annotation");
this.ignoredTypes = extract(attributes, "ignored", "ignoredType");
this.ignoredTypes = resolveWhenPossible(extract(attributes, "ignored", "ignoredType"));
this.parameterizedContainers = resolveWhenPossible(extract(attributes, "parameterizedContainer"));
this.strategy = annotation.getValue("search", SearchStrategy.class).orElse(null);
Set<String> types = extractTypes(attributes);
Set<ResolvableType> types = resolveWhenPossible(extractTypes(attributes));
BeanTypeDeductionException deductionException = null;
if (types.isEmpty() && this.names.isEmpty() && this.annotations.isEmpty()) {
try {
@@ -614,17 +604,18 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
Collections.addAll(result, additional);
}
private Set<Class<?>> resolveWhenPossible(Set<String> classNames) {
private Set<ResolvableType> resolveWhenPossible(Set<String> classNames) {
if (classNames.isEmpty()) {
return Collections.emptySet();
}
Set<Class<?>> resolved = new LinkedHashSet<>(classNames.size());
Set<ResolvableType> resolved = new LinkedHashSet<>(classNames.size());
for (String className : classNames) {
try {
resolved.add(resolve(className, this.context.getClassLoader()));
Class<?> type = resolve(className, this.context.getClassLoader());
resolved.add(ResolvableType.forRawClass(type));
}
catch (ClassNotFoundException | NoClassDefFoundError ex) {
// Ignore
resolved.add(ResolvableType.NONE);
}
}
return resolved;
@@ -653,48 +644,44 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return "@" + ClassUtils.getShortName(this.annotationType);
}
private Set<String> deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) {
private Set<ResolvableType> deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) {
return deducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata);
}
return Collections.emptySet();
}
private Set<String> deducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata) {
private Set<ResolvableType> deducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata) {
try {
Class<?> returnType = getReturnType(context, metadata);
return Collections.singleton(returnType.getName());
return Set.of(getReturnType(context, metadata));
}
catch (Throwable ex) {
throw new BeanTypeDeductionException(metadata.getDeclaringClassName(), metadata.getMethodName(), ex);
}
}
private Class<?> getReturnType(ConditionContext context, MethodMetadata metadata)
private ResolvableType getReturnType(ConditionContext context, MethodMetadata metadata)
throws ClassNotFoundException, LinkageError {
// Safe to load at this point since we are in the REGISTER_BEAN phase
ClassLoader classLoader = context.getClassLoader();
Class<?> returnType = resolve(metadata.getReturnTypeName(), classLoader);
if (isParameterizedContainer(returnType)) {
returnType = getReturnTypeGeneric(metadata, classLoader);
ResolvableType returnType = getMethodReturnType(metadata, classLoader);
if (isParameterizedContainer(returnType.resolve())) {
returnType = returnType.getGeneric();
}
return returnType;
}
private boolean isParameterizedContainer(Class<?> type) {
for (Class<?> parameterizedContainer : this.parameterizedContainers) {
if (parameterizedContainer.isAssignableFrom(type)) {
return true;
}
}
return false;
return (type != null) && this.parameterizedContainers.stream()
.map(ResolvableType::resolve)
.anyMatch((container) -> container != null && container.isAssignableFrom(type));
}
private Class<?> getReturnTypeGeneric(MethodMetadata metadata, ClassLoader classLoader)
private ResolvableType getMethodReturnType(MethodMetadata metadata, ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
Class<?> declaringClass = resolve(metadata.getDeclaringClassName(), classLoader);
Method beanMethod = findBeanMethod(declaringClass, metadata.getMethodName());
return ResolvableType.forMethodReturnType(beanMethod).resolveGeneric();
return ResolvableType.forMethodReturnType(beanMethod);
}
private Method findBeanMethod(Class<?> declaringClass, String methodName) {
@@ -720,6 +707,10 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return (this.strategy != null) ? this.strategy : SearchStrategy.ALL;
}
Set<ResolvableType> getTypes() {
return this.types;
}
private ConditionContext getContext() {
return this.context;
}
@@ -728,19 +719,15 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
return this.names;
}
protected Set<String> getTypes() {
return this.types;
}
private Set<String> getAnnotations() {
return this.annotations;
}
private Set<String> getIgnoredTypes() {
private Set<ResolvableType> getIgnoredTypes() {
return this.ignoredTypes;
}
private Set<Class<?>> getParameterizedContainers() {
private Set<ResolvableType> getParameterizedContainers() {
return this.parameterizedContainers;
}
@@ -847,13 +834,13 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat
this.unmatchedAnnotations.add(annotation);
}
private void recordMatchedType(String type, Collection<String> matchingNames) {
this.matchedTypes.put(type, matchingNames);
private void recordMatchedType(ResolvableType type, Collection<String> matchingNames) {
this.matchedTypes.put(type.toString(), matchingNames);
this.namesOfAllMatches.addAll(matchingNames);
}
private void recordUnmatchedType(String type) {
this.unmatchedTypes.add(type);
private void recordUnmatchedType(ResolvableType type) {
this.unmatchedTypes.add(type.toString());
}
boolean isAllMatched() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-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.
@@ -52,6 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Uladzislau Seuruk
*/
class ConditionalOnBeanTests {
@@ -116,8 +117,8 @@ class ConditionalOnBeanTests {
@Test
void withPropertyPlaceholderClassName() {
this.contextRunner
.withUserConfiguration(PropertySourcesPlaceholderConfigurer.class, WithPropertyPlaceholderClassName.class,
OnBeanClassConfiguration.class)
.withUserConfiguration(PropertySourcesPlaceholderConfigurer.class,
WithPropertyPlaceholderClassNameConfiguration.class, OnBeanClassConfiguration.class)
.withPropertyValues("mybeanclass=java.lang.String")
.run((context) -> assertThat(context).hasNotFailed());
}
@@ -165,7 +166,8 @@ class ConditionalOnBeanTests {
@Test
void conditionEvaluationConsidersChangeInTypeWhenBeanIsOverridden() {
this.contextRunner.withAllowBeanDefinitionOverriding(true)
.withUserConfiguration(OriginalDefinition.class, OverridingDefinition.class, ConsumingConfiguration.class)
.withUserConfiguration(OriginalDefinitionConfiguration.class, OverridingDefinitionConfiguration.class,
ConsumingConfiguration.class)
.run((context) -> {
assertThat(context).hasBean("testBean");
assertThat(context).hasSingleBean(Integer.class);
@@ -176,69 +178,73 @@ class ConditionalOnBeanTests {
@Test
void parameterizedContainerWhenValueIsOfMissingBeanDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithoutCustomConfig.class, ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("otherExampleBean")));
.withUserConfiguration(ParameterizedWithoutCustomConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "otherExampleBean")));
}
@Test
void parameterizedContainerWhenValueIsOfExistingBeanMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomConfig.class, ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("customExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithCustomConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "customExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenValueIsOfMissingBeanRegistrationDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithoutCustomContainerConfig.class,
ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("otherExampleBean")));
.withUserConfiguration(ParameterizedWithoutCustomContainerConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "otherExampleBean")));
}
@Test
void parameterizedContainerWhenValueIsOfExistingBeanRegistrationMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomContainerConfig.class,
ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("customExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithCustomContainerConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "customExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenReturnTypeIsOfExistingBeanMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomConfig.class,
ParameterizedConditionWithReturnTypeConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("customExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithCustomConfiguration.class,
ParameterizedConditionWithReturnTypeConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "customExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenReturnTypeIsOfExistingBeanRegistrationMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomContainerConfig.class,
ParameterizedConditionWithReturnTypeConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("customExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithCustomContainerConfiguration.class,
ParameterizedConditionWithReturnTypeConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "customExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenReturnRegistrationTypeIsOfExistingBeanMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomConfig.class,
ParameterizedConditionWithReturnRegistrationTypeConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("customExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithCustomConfiguration.class,
ParameterizedConditionWithReturnRegistrationTypeConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "customExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenReturnRegistrationTypeIsOfExistingBeanRegistrationMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomContainerConfig.class,
ParameterizedConditionWithReturnRegistrationTypeConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("customExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithCustomContainerConfiguration.class,
ParameterizedConditionWithReturnRegistrationTypeConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "customExampleBean", "conditionalCustomExampleBean")));
}
@Test
@@ -257,7 +263,7 @@ class ConditionalOnBeanTests {
@Test
void conditionalOnAnnotatedBeanIgnoresNotAutowireCandidateBean() {
this.contextRunner
.withUserConfiguration(AnnotatedNotAutowireCandidateConfig.class, OnAnnotationConfiguration.class)
.withUserConfiguration(AnnotatedNotAutowireCandidateConfiguration.class, OnAnnotationConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
}
@@ -284,13 +290,66 @@ class ConditionalOnBeanTests {
@Test
void conditionalOnAnnotatedBeanIgnoresNotDefaultCandidateBean() {
this.contextRunner
.withUserConfiguration(AnnotatedNotDefaultCandidateConfig.class, OnAnnotationConfiguration.class)
.withUserConfiguration(AnnotatedNotDefaultCandidateConfiguration.class, OnAnnotationConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
}
private Consumer<ConfigurableApplicationContext> exampleBeanRequirement(String... names) {
@Test
void genericWhenTypeArgumentMatches() {
this.contextRunner.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
GenericWithStringTypeArgumentsConfiguration.class, GenericWithIntegerTypeArgumentsConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"customGenericExampleBean", "genericStringTypeArgumentsExampleBean")));
}
@Test
void genericWhenTypeArgumentWithValueMatches() {
this.contextRunner
.withUserConfiguration(GenericWithStringConfiguration.class,
TypeArgumentsConditionWithValueConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"genericStringExampleBean", "genericStringWithValueExampleBean")));
}
@Test
void genericWithValueWhenSubclassTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
TypeArgumentsConditionWithValueConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"customGenericExampleBean", "genericStringWithValueExampleBean")));
}
@Test
void parameterizedContainerGenericWhenTypeArgumentNotMatches() {
this.contextRunner
.withUserConfiguration(GenericWithIntegerConfiguration.class,
TypeArgumentsConditionWithParameterizedContainerConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(GenericExampleBean.class, "genericIntegerExampleBean")));
}
@Test
void parameterizedContainerGenericWhenTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(GenericWithStringConfiguration.class,
TypeArgumentsConditionWithParameterizedContainerConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"genericStringExampleBean", "parameterizedContainerGenericExampleBean")));
}
@Test
void parameterizedContainerGenericWhenSubclassTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
TypeArgumentsConditionWithParameterizedContainerConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"customGenericExampleBean", "parameterizedContainerGenericExampleBean")));
}
private Consumer<ConfigurableApplicationContext> beansAndContainersNamed(Class<?> type, String... names) {
return (context) -> {
String[] beans = context.getBeanNamesForType(ExampleBean.class);
String[] beans = context.getBeanNamesForType(type);
String[] containers = context.getBeanNamesForType(TestParameterizedContainer.class);
assertThat(StringUtils.concatenateStringArrays(beans, containers)).containsOnly(names);
};
@@ -429,7 +488,7 @@ class ConditionalOnBeanTests {
@Configuration(proxyBeanMethods = false)
@Import(WithPropertyPlaceholderClassNameRegistrar.class)
static class WithPropertyPlaceholderClassName {
static class WithPropertyPlaceholderClassNameConfiguration {
}
@@ -495,27 +554,8 @@ class ConditionalOnBeanTests {
}
static class ExampleFactoryBean implements FactoryBean<ExampleBean> {
@Override
public ExampleBean getObject() {
return new ExampleBean("fromFactory");
}
@Override
public Class<?> getObjectType() {
return ExampleBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
@Configuration(proxyBeanMethods = false)
static class OriginalDefinition {
static class OriginalDefinitionConfiguration {
@Bean
String testBean() {
@@ -526,7 +566,7 @@ class ConditionalOnBeanTests {
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(String.class)
static class OverridingDefinition {
static class OverridingDefinitionConfiguration {
@Bean
Integer testBean() {
@@ -545,7 +585,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomConfig {
static class ParameterizedWithCustomConfiguration {
@Bean
CustomExampleBean customExampleBean() {
@@ -555,7 +595,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithoutCustomConfig {
static class ParameterizedWithoutCustomConfiguration {
@Bean
OtherExampleBean otherExampleBean() {
@@ -565,7 +605,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithoutCustomContainerConfig {
static class ParameterizedWithoutCustomContainerConfiguration {
@Bean
TestParameterizedContainer<OtherExampleBean> otherExampleBean() {
@@ -575,7 +615,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomContainerConfig {
static class ParameterizedWithCustomContainerConfiguration {
@Bean
TestParameterizedContainer<CustomExampleBean> customExampleBean() {
@@ -585,7 +625,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithValueConfig {
static class ParameterizedConditionWithValueConfiguration {
@Bean
@ConditionalOnBean(value = CustomExampleBean.class, parameterizedContainer = TestParameterizedContainer.class)
@@ -596,7 +636,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithReturnTypeConfig {
static class ParameterizedConditionWithReturnTypeConfiguration {
@Bean
@ConditionalOnBean(parameterizedContainer = TestParameterizedContainer.class)
@@ -607,7 +647,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithReturnRegistrationTypeConfig {
static class ParameterizedConditionWithReturnRegistrationTypeConfiguration {
@Bean
@ConditionalOnBean(parameterizedContainer = TestParameterizedContainer.class)
@@ -618,7 +658,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class AnnotatedNotAutowireCandidateConfig {
static class AnnotatedNotAutowireCandidateConfiguration {
@Bean(autowireCandidate = false)
ExampleBean exampleBean() {
@@ -628,7 +668,7 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class AnnotatedNotDefaultCandidateConfig {
static class AnnotatedNotDefaultCandidateConfiguration {
@Bean(defaultCandidate = false)
ExampleBean exampleBean() {
@@ -637,6 +677,99 @@ class ConditionalOnBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomGenericConfiguration {
@Bean
CustomGenericExampleBean customGenericExampleBean() {
return new CustomGenericExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class GenericWithStringConfiguration {
@Bean
GenericExampleBean<String> genericStringExampleBean() {
return new GenericExampleBean<>("genericStringExampleBean");
}
}
@Configuration(proxyBeanMethods = false)
static class GenericWithStringTypeArgumentsConfiguration {
@Bean
@ConditionalOnBean
GenericExampleBean<String> genericStringTypeArgumentsExampleBean() {
return new GenericExampleBean<>("genericStringTypeArgumentsExampleBean");
}
}
@Configuration(proxyBeanMethods = false)
static class GenericWithIntegerConfiguration {
@Bean
GenericExampleBean<Integer> genericIntegerExampleBean() {
return new GenericExampleBean<>(1_000);
}
}
@Configuration(proxyBeanMethods = false)
static class GenericWithIntegerTypeArgumentsConfiguration {
@Bean
@ConditionalOnBean
GenericExampleBean<Integer> genericIntegerTypeArgumentsExampleBean() {
return new GenericExampleBean<>(1_000);
}
}
@Configuration(proxyBeanMethods = false)
static class TypeArgumentsConditionWithValueConfiguration {
@Bean
@ConditionalOnBean(GenericExampleBean.class)
GenericExampleBean<String> genericStringWithValueExampleBean() {
return new GenericExampleBean<>("genericStringWithValueExampleBean");
}
}
@Configuration(proxyBeanMethods = false)
static class TypeArgumentsConditionWithParameterizedContainerConfiguration {
@Bean
@ConditionalOnBean(parameterizedContainer = TestParameterizedContainer.class)
TestParameterizedContainer<GenericExampleBean<String>> parameterizedContainerGenericExampleBean() {
return new TestParameterizedContainer<>();
}
}
static class ExampleFactoryBean implements FactoryBean<ExampleBean> {
@Override
public ExampleBean getObject() {
return new ExampleBean("fromFactory");
}
@Override
public Class<?> getObjectType() {
return ExampleBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
@TestAnnotation
static class ExampleBean {
@@ -669,6 +802,30 @@ class ConditionalOnBeanTests {
}
@TestAnnotation
static class GenericExampleBean<T> {
private final T value;
GenericExampleBean(T value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(this.value);
}
}
static class CustomGenericExampleBean extends GenericExampleBean<String> {
CustomGenericExampleBean() {
super("custom subclass");
}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-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.
@@ -61,6 +61,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Phillip Webb
* @author Jakub Kubrynski
* @author Andy Wilkinson
* @author Uladzislau Seuruk
*/
@SuppressWarnings("resource")
class ConditionalOnMissingBeanTests {
@@ -100,7 +101,7 @@ class ConditionalOnMissingBeanTests {
void hierarchyConsidered() {
this.contextRunner.withUserConfiguration(FooConfiguration.class)
.run((parent) -> new ApplicationContextRunner().withParent(parent)
.withUserConfiguration(HierarchyConsidered.class)
.withUserConfiguration(HierarchyConsideredConfiguration.class)
.run((context) -> assertThat(context.containsLocalBean("bar")).isFalse()));
}
@@ -108,13 +109,13 @@ class ConditionalOnMissingBeanTests {
void hierarchyNotConsidered() {
this.contextRunner.withUserConfiguration(FooConfiguration.class)
.run((parent) -> new ApplicationContextRunner().withParent(parent)
.withUserConfiguration(HierarchyNotConsidered.class)
.withUserConfiguration(HierarchyNotConsideredConfiguration.class)
.run((context) -> assertThat(context.containsLocalBean("bar")).isTrue()));
}
@Test
void impliedOnBeanMethod() {
this.contextRunner.withUserConfiguration(ExampleBeanConfiguration.class, ImpliedOnBeanMethod.class)
this.contextRunner.withUserConfiguration(ExampleBeanConfiguration.class, ImpliedOnBeanMethodConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(ExampleBean.class));
}
@@ -173,7 +174,8 @@ class ConditionalOnMissingBeanTests {
@Test
void testOnMissingBeanConditionWithFactoryBean() {
this.contextRunner
.withUserConfiguration(FactoryBeanConfiguration.class, ConditionalOnMissingBeanProducedByFactoryBean.class,
.withUserConfiguration(FactoryBeanConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@@ -182,7 +184,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithComponentScannedFactoryBean() {
this.contextRunner
.withUserConfiguration(ComponentScannedFactoryBeanBeanMethodConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ScanBean.class)).hasToString("fromFactory"));
}
@@ -190,7 +193,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithComponentScannedFactoryBeanWithBeanMethodArguments() {
this.contextRunner
.withUserConfiguration(ComponentScannedFactoryBeanBeanMethodWithArgumentsConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ScanBean.class)).hasToString("fromFactory"));
}
@@ -198,7 +202,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithFactoryBeanWithBeanMethodArguments() {
this.contextRunner
.withUserConfiguration(FactoryBeanWithBeanMethodArgumentsConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.withPropertyValues("theValue=foo")
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@@ -207,7 +212,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithConcreteFactoryBean() {
this.contextRunner
.withUserConfiguration(ConcreteFactoryBeanConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@@ -216,7 +222,8 @@ class ConditionalOnMissingBeanTests {
// We could not tell that the FactoryBean would ultimately create an ExampleBean
this.contextRunner
.withUserConfiguration(UnhelpfulFactoryBeanConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context).getBeans(ExampleBean.class).hasSize(2));
}
@@ -224,7 +231,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithRegisteredFactoryBean() {
this.contextRunner
.withUserConfiguration(RegisteredFactoryBeanConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@@ -232,7 +240,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithNonspecificFactoryBeanWithClassAttribute() {
this.contextRunner
.withUserConfiguration(NonspecificFactoryBeanClassAttributeConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@@ -240,7 +249,8 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithNonspecificFactoryBeanWithStringAttribute() {
this.contextRunner
.withUserConfiguration(NonspecificFactoryBeanStringAttributeConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@@ -248,15 +258,16 @@ class ConditionalOnMissingBeanTests {
void testOnMissingBeanConditionWithFactoryBeanInXml() {
this.contextRunner
.withUserConfiguration(FactoryBeanXmlConfiguration.class,
ConditionalOnMissingBeanProducedByFactoryBean.class, PropertyPlaceholderAutoConfiguration.class)
ConditionalOnMissingBeanProducedByFactoryBeanConfiguration.class,
PropertyPlaceholderAutoConfiguration.class)
.run((context) -> assertThat(context.getBean(ExampleBean.class)).hasToString("fromFactory"));
}
@Test
void testOnMissingBeanConditionWithIgnoredSubclass() {
this.contextRunner
.withUserConfiguration(CustomExampleBeanConfiguration.class, ConditionalOnIgnoredSubclass.class,
PropertyPlaceholderAutoConfiguration.class)
.withUserConfiguration(CustomExampleBeanConfiguration.class,
ConditionalOnIgnoredSubclassConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.run((context) -> {
assertThat(context).getBeans(ExampleBean.class).hasSize(2);
assertThat(context).getBeans(CustomExampleBean.class).hasSize(1);
@@ -266,8 +277,8 @@ class ConditionalOnMissingBeanTests {
@Test
void testOnMissingBeanConditionWithIgnoredSubclassByName() {
this.contextRunner
.withUserConfiguration(CustomExampleBeanConfiguration.class, ConditionalOnIgnoredSubclassByName.class,
PropertyPlaceholderAutoConfiguration.class)
.withUserConfiguration(CustomExampleBeanConfiguration.class,
ConditionalOnIgnoredSubclassByNameConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.run((context) -> {
assertThat(context).getBeans(ExampleBean.class).hasSize(2);
assertThat(context).getBeans(CustomExampleBean.class).hasSize(1);
@@ -304,89 +315,97 @@ class ConditionalOnMissingBeanTests {
@Test
void parameterizedContainerWhenValueIsOfMissingBeanMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithoutCustomConfig.class, ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("otherExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithoutCustomConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "otherExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenValueIsOfExistingBeanDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomConfig.class, ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("customExampleBean")));
.withUserConfiguration(ParameterizedWithCustomConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "customExampleBean")));
}
@Test
void parameterizedContainerWhenValueIsOfMissingBeanRegistrationMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithoutCustomContainerConfig.class,
ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context)
.satisfies(exampleBeanRequirement("otherExampleBean", "conditionalCustomExampleBean")));
.withUserConfiguration(ParameterizedWithoutCustomContainerConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context).satisfies(
beansAndContainersNamed(ExampleBean.class, "otherExampleBean", "conditionalCustomExampleBean")));
}
@Test
void parameterizedContainerWhenValueIsOfExistingBeanRegistrationDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomContainerConfig.class,
ParameterizedConditionWithValueConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("customExampleBean")));
.withUserConfiguration(ParameterizedWithCustomContainerConfiguration.class,
ParameterizedConditionWithValueConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "customExampleBean")));
}
@Test
void parameterizedContainerWhenReturnTypeIsOfExistingBeanDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomConfig.class,
ParameterizedConditionWithReturnTypeConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("customExampleBean")));
.withUserConfiguration(ParameterizedWithCustomConfiguration.class,
ParameterizedConditionWithReturnTypeConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "customExampleBean")));
}
@Test
void parameterizedContainerWhenReturnTypeIsOfExistingBeanRegistrationDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomContainerConfig.class,
ParameterizedConditionWithReturnTypeConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("customExampleBean")));
.withUserConfiguration(ParameterizedWithCustomContainerConfiguration.class,
ParameterizedConditionWithReturnTypeConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "customExampleBean")));
}
@Test
void parameterizedContainerWhenReturnRegistrationTypeIsOfExistingBeanDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomConfig.class,
ParameterizedConditionWithReturnRegistrationTypeConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("customExampleBean")));
.withUserConfiguration(ParameterizedWithCustomConfiguration.class,
ParameterizedConditionWithReturnRegistrationTypeConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "customExampleBean")));
}
@Test
void parameterizedContainerWhenReturnRegistrationTypeIsOfExistingBeanRegistrationDoesNotMatch() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomContainerConfig.class,
ParameterizedConditionWithReturnRegistrationTypeConfig.class)
.run((context) -> assertThat(context).satisfies(exampleBeanRequirement("customExampleBean")));
.withUserConfiguration(ParameterizedWithCustomContainerConfiguration.class,
ParameterizedConditionWithReturnRegistrationTypeConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(ExampleBean.class, "customExampleBean")));
}
@Test
void typeBasedMatchingIgnoresBeanThatIsNotAutowireCandidate() {
this.contextRunner.withUserConfiguration(NotAutowireCandidateConfig.class, OnBeanTypeConfiguration.class)
this.contextRunner.withUserConfiguration(NotAutowireCandidateConfiguration.class, OnBeanTypeConfiguration.class)
.run((context) -> assertThat(context).hasBean("bar"));
}
@Test
void nameBasedMatchingConsidersBeanThatIsNotAutowireCandidate() {
this.contextRunner.withUserConfiguration(NotAutowireCandidateConfig.class, OnBeanNameConfiguration.class)
this.contextRunner.withUserConfiguration(NotAutowireCandidateConfiguration.class, OnBeanNameConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
}
@Test
void annotationBasedMatchingIgnoresBeanThatIsNotAutowireCandidateBean() {
this.contextRunner
.withUserConfiguration(AnnotatedNotAutowireCandidateConfig.class, OnAnnotationConfiguration.class)
.withUserConfiguration(AnnotatedNotAutowireCandidateConfiguration.class, OnAnnotationConfiguration.class)
.run((context) -> assertThat(context).hasBean("bar"));
}
@Test
void typeBasedMatchingIgnoresBeanThatIsNotDefaultCandidate() {
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfig.class, OnBeanTypeConfiguration.class)
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfiguration.class, OnBeanTypeConfiguration.class)
.run((context) -> assertThat(context).hasBean("bar"));
}
@@ -394,27 +413,99 @@ class ConditionalOnMissingBeanTests {
void typeBasedMatchingIgnoresFactoryBeanThatIsNotDefaultCandidate() {
this.contextRunner
.withUserConfiguration(NotDefaultCandidateFactoryBeanConfiguration.class,
ConditionalOnMissingFactoryBean.class)
ConditionalOnMissingFactoryBeanConfiguration.class)
.run((context) -> assertThat(context).hasBean("&exampleFactoryBean")
.hasBean("&additionalExampleFactoryBean"));
}
@Test
void nameBasedMatchingConsidersBeanThatIsNotDefaultCandidate() {
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfig.class, OnBeanNameConfiguration.class)
this.contextRunner.withUserConfiguration(NotDefaultCandidateConfiguration.class, OnBeanNameConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean("bar"));
}
@Test
void annotationBasedMatchingIgnoresBeanThatIsNotDefaultCandidateBean() {
this.contextRunner
.withUserConfiguration(AnnotatedNotDefaultCandidateConfig.class, OnAnnotationConfiguration.class)
.withUserConfiguration(AnnotatedNotDefaultCandidateConfiguration.class, OnAnnotationConfiguration.class)
.run((context) -> assertThat(context).hasBean("bar"));
}
private Consumer<ConfigurableApplicationContext> exampleBeanRequirement(String... names) {
@Test
void genericWhenTypeArgumentNotMatches() {
this.contextRunner
.withUserConfiguration(GenericWithStringTypeArgumentsConfiguration.class,
GenericWithIntegerTypeArgumentsConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"genericStringExampleBean", "genericIntegerExampleBean")));
}
@Test
void genericWhenSubclassTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
GenericWithStringTypeArgumentsConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(GenericExampleBean.class, "customGenericExampleBean")));
}
@Test
void genericWhenSubclassTypeArgumentNotMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
GenericWithIntegerTypeArgumentsConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"customGenericExampleBean", "genericIntegerExampleBean")));
}
@Test
void genericWhenTypeArgumentWithValueMatches() {
this.contextRunner
.withUserConfiguration(GenericWithStringTypeArgumentsConfiguration.class,
TypeArgumentsConditionWithValueConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(GenericExampleBean.class, "genericStringExampleBean")));
}
@Test
void genericWithValueWhenSubclassTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
TypeArgumentsConditionWithValueConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(GenericExampleBean.class, "customGenericExampleBean")));
}
@Test
void parameterizedContainerGenericWhenTypeArgumentNotMatches() {
this.contextRunner
.withUserConfiguration(GenericWithIntegerTypeArgumentsConfiguration.class,
TypeArgumentsConditionWithParameterizedContainerConfiguration.class)
.run((context) -> assertThat(context).satisfies(beansAndContainersNamed(GenericExampleBean.class,
"genericIntegerExampleBean", "parameterizedContainerGenericExampleBean")));
}
@Test
void parameterizedContainerGenericWhenTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(GenericWithStringTypeArgumentsConfiguration.class,
TypeArgumentsConditionWithParameterizedContainerConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(GenericExampleBean.class, "genericStringExampleBean")));
}
@Test
void parameterizedContainerGenericWhenSubclassTypeArgumentMatches() {
this.contextRunner
.withUserConfiguration(ParameterizedWithCustomGenericConfiguration.class,
TypeArgumentsConditionWithParameterizedContainerConfiguration.class)
.run((context) -> assertThat(context)
.satisfies(beansAndContainersNamed(GenericExampleBean.class, "customGenericExampleBean")));
}
private Consumer<ConfigurableApplicationContext> beansAndContainersNamed(Class<?> type, String... names) {
return (context) -> {
String[] beans = context.getBeanNamesForType(ExampleBean.class);
String[] beans = context.getBeanNamesForType(type);
String[] containers = context.getBeanNamesForType(TestParameterizedContainer.class);
assertThat(StringUtils.concatenateStringArrays(beans, containers)).containsOnly(names);
};
@@ -584,7 +675,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ConditionalOnMissingBeanProducedByFactoryBean {
static class ConditionalOnMissingBeanProducedByFactoryBeanConfiguration {
@Bean
@ConditionalOnMissingBean
@@ -595,7 +686,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ConditionalOnMissingFactoryBean {
static class ConditionalOnMissingFactoryBeanConfiguration {
@Bean
@ConditionalOnMissingBean
@@ -606,7 +697,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ConditionalOnIgnoredSubclass {
static class ConditionalOnIgnoredSubclassConfiguration {
@Bean
@ConditionalOnMissingBean(ignored = CustomExampleBean.class)
@@ -617,7 +708,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ConditionalOnIgnoredSubclassByName {
static class ConditionalOnIgnoredSubclassByNameConfiguration {
@Bean
@ConditionalOnMissingBean(
@@ -683,7 +774,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class NotAutowireCandidateConfig {
static class NotAutowireCandidateConfiguration {
@Bean(autowireCandidate = false)
String foo() {
@@ -693,7 +784,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class NotDefaultCandidateConfig {
static class NotDefaultCandidateConfiguration {
@Bean(defaultCandidate = false)
String foo() {
@@ -704,7 +795,7 @@ class ConditionalOnMissingBeanTests {
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = "foo")
static class HierarchyConsidered {
static class HierarchyConsideredConfiguration {
@Bean
String bar() {
@@ -715,7 +806,7 @@ class ConditionalOnMissingBeanTests {
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = "foo", search = SearchStrategy.CURRENT)
static class HierarchyNotConsidered {
static class HierarchyNotConsideredConfiguration {
@Bean
String bar() {
@@ -751,7 +842,7 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ImpliedOnBeanMethod {
static class ImpliedOnBeanMethodConfiguration {
@Bean
@ConditionalOnMissingBean
@@ -761,6 +852,153 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomConfiguration {
@Bean
CustomExampleBean customExampleBean() {
return new CustomExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithoutCustomConfiguration {
@Bean
OtherExampleBean otherExampleBean() {
return new OtherExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithoutCustomContainerConfiguration {
@Bean
TestParameterizedContainer<OtherExampleBean> otherExampleBean() {
return new TestParameterizedContainer<>();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomContainerConfiguration {
@Bean
TestParameterizedContainer<CustomExampleBean> customExampleBean() {
return new TestParameterizedContainer<>();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithValueConfiguration {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
CustomExampleBean conditionalCustomExampleBean() {
return new CustomExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithReturnTypeConfiguration {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
CustomExampleBean conditionalCustomExampleBean() {
return new CustomExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithReturnRegistrationTypeConfiguration {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
TestParameterizedContainer<CustomExampleBean> conditionalCustomExampleBean() {
return new TestParameterizedContainer<>();
}
}
@Configuration(proxyBeanMethods = false)
static class AnnotatedNotAutowireCandidateConfiguration {
@Bean(autowireCandidate = false)
ExampleBean exampleBean() {
return new ExampleBean("value");
}
}
@Configuration(proxyBeanMethods = false)
static class AnnotatedNotDefaultCandidateConfiguration {
@Bean(autowireCandidate = false)
ExampleBean exampleBean() {
return new ExampleBean("value");
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomGenericConfiguration {
@Bean
CustomGenericExampleBean customGenericExampleBean() {
return new CustomGenericExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class GenericWithStringTypeArgumentsConfiguration {
@Bean
@ConditionalOnMissingBean
GenericExampleBean<String> genericStringExampleBean() {
return new GenericExampleBean<>("genericStringExampleBean");
}
}
@Configuration(proxyBeanMethods = false)
static class GenericWithIntegerTypeArgumentsConfiguration {
@Bean
@ConditionalOnMissingBean
GenericExampleBean<Integer> genericIntegerExampleBean() {
return new GenericExampleBean<>(1_000);
}
}
@Configuration(proxyBeanMethods = false)
static class TypeArgumentsConditionWithValueConfiguration {
@Bean
@ConditionalOnMissingBean(GenericExampleBean.class)
GenericExampleBean<String> genericStringWithValueExampleBean() {
return new GenericExampleBean<>("genericStringWithValueExampleBean");
}
}
@Configuration(proxyBeanMethods = false)
static class TypeArgumentsConditionWithParameterizedContainerConfiguration {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
TestParameterizedContainer<GenericExampleBean<String>> parameterizedContainerGenericExampleBean() {
return new TestParameterizedContainer<>();
}
}
static class ExampleFactoryBean implements FactoryBean<ExampleBean> {
ExampleFactoryBean(String value) {
@@ -807,99 +1045,6 @@ class ConditionalOnMissingBeanTests {
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomConfig {
@Bean
CustomExampleBean customExampleBean() {
return new CustomExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithoutCustomConfig {
@Bean
OtherExampleBean otherExampleBean() {
return new OtherExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithoutCustomContainerConfig {
@Bean
TestParameterizedContainer<OtherExampleBean> otherExampleBean() {
return new TestParameterizedContainer<>();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedWithCustomContainerConfig {
@Bean
TestParameterizedContainer<CustomExampleBean> customExampleBean() {
return new TestParameterizedContainer<>();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithValueConfig {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
CustomExampleBean conditionalCustomExampleBean() {
return new CustomExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithReturnTypeConfig {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
CustomExampleBean conditionalCustomExampleBean() {
return new CustomExampleBean();
}
}
@Configuration(proxyBeanMethods = false)
static class ParameterizedConditionWithReturnRegistrationTypeConfig {
@Bean
@ConditionalOnMissingBean(parameterizedContainer = TestParameterizedContainer.class)
TestParameterizedContainer<CustomExampleBean> conditionalCustomExampleBean() {
return new TestParameterizedContainer<>();
}
}
@Configuration(proxyBeanMethods = false)
static class AnnotatedNotAutowireCandidateConfig {
@Bean(autowireCandidate = false)
ExampleBean exampleBean() {
return new ExampleBean("value");
}
}
@Configuration(proxyBeanMethods = false)
static class AnnotatedNotDefaultCandidateConfig {
@Bean(autowireCandidate = false)
ExampleBean exampleBean() {
return new ExampleBean("value");
}
}
@TestAnnotation
static class ExampleBean {
@@ -956,6 +1101,30 @@ class ConditionalOnMissingBeanTests {
}
@TestAnnotation
static class GenericExampleBean<T> {
private final T value;
GenericExampleBean(T value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(this.value);
}
}
static class CustomGenericExampleBean extends GenericExampleBean<String> {
CustomGenericExampleBean() {
super("custom subclass");
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented