diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index a53c64fcda7..5bbf83fcfb0 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -213,11 +213,11 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat ConfigurableListableBeanFactory beanFactory = getSearchBeanFactory(spec); ClassLoader classLoader = spec.getContext().getClassLoader(); boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT; - Set parameterizedContainers = spec.getParameterizedContainers(); + Set parameterizedContainers = spec.getParameterizedContainers(); MatchResult result = new MatchResult(); Set beansIgnoredByType = getNamesOfBeansIgnoredByType(beanFactory, considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers); - for (ResolvableType type : spec.getTypes()) { + for (BeanType type : spec.getTypes()) { Map typeMatchedDefinitions = getBeanDefinitionsForType(beanFactory, considerHierarchy, type, parameterizedContainers); Set typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions, @@ -307,9 +307,9 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } private Set getNamesOfBeansIgnoredByType(ListableBeanFactory beanFactory, boolean considerHierarchy, - Set ignoredTypes, Set parameterizedContainers) { + Set ignoredTypes, Set parameterizedContainers) { Set result = null; - for (ResolvableType ignoredType : ignoredTypes) { + for (BeanType ignoredType : ignoredTypes) { Collection ignoredNames = getBeanDefinitionsForType(beanFactory, considerHierarchy, ignoredType, parameterizedContainers) .keySet(); @@ -319,20 +319,20 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } private Map getBeanDefinitionsForType(ListableBeanFactory beanFactory, - boolean considerHierarchy, ResolvableType type, Set parameterizedContainers) { + boolean considerHierarchy, BeanType type, Set parameterizedContainers) { Map result = collectBeanDefinitionsForType(beanFactory, considerHierarchy, type, parameterizedContainers, null); return (result != null) ? result : Collections.emptyMap(); } private @Nullable Map collectBeanDefinitionsForType( - ListableBeanFactory beanFactory, boolean considerHierarchy, ResolvableType type, - Set parameterizedContainers, @Nullable Map result) { - result = putAll(result, beanFactory.getBeanNamesForType(type, true, false), beanFactory); - for (ResolvableType parameterizedContainer : parameterizedContainers) { - Class resolved = parameterizedContainer.resolve(); + ListableBeanFactory beanFactory, boolean considerHierarchy, BeanType type, + Set parameterizedContainers, @Nullable Map result) { + result = putAll(result, beanFactory.getBeanNamesForType(type.resolvableType(), true, false), beanFactory); + for (BeanType parameterizedContainer : parameterizedContainers) { + Class resolved = parameterizedContainer.resolvableType().resolve(); Assert.state(resolved != null, "'resolved' must not be null"); - ResolvableType generic = ResolvableType.forClassWithGenerics(resolved, type); + ResolvableType generic = ResolvableType.forClassWithGenerics(resolved, type.resolvableType()); result = putAll(result, beanFactory.getBeanNamesForType(generic, true, false), beanFactory); } if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory hierarchicalBeanFactory) { @@ -563,13 +563,13 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat private final Set names; - private final Set types; + private final Set types; private final Set annotations; - private final Set ignoredTypes; + private final Set ignoredTypes; - private final Set parameterizedContainers; + private final Set parameterizedContainers; private final @Nullable SearchStrategy strategy; @@ -586,7 +586,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat this.ignoredTypes = resolveWhenPossible(extract(attributes, "ignored", "ignoredType")); this.parameterizedContainers = resolveWhenPossible(extract(attributes, "parameterizedContainer")); this.strategy = annotation.getValue("search", SearchStrategy.class).orElse(null); - Set types = resolveWhenPossible(extractTypes(attributes)); + Set types = resolveWhenPossible(extractTypes(attributes)); BeanTypeDeductionException deductionException = null; if (types.isEmpty() && this.names.isEmpty() && this.annotations.isEmpty()) { try { @@ -628,18 +628,18 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat Collections.addAll(result, additional); } - private Set resolveWhenPossible(Set classNames) { + private Set resolveWhenPossible(Set classNames) { if (classNames.isEmpty()) { return Collections.emptySet(); } - Set resolved = new LinkedHashSet<>(classNames.size()); + Set resolved = new LinkedHashSet<>(classNames.size()); for (String className : classNames) { try { Class type = resolve(className, this.context.getClassLoader()); - resolved.add(ResolvableType.forRawClass(type)); + resolved.add(new BeanType(className, ResolvableType.forRawClass(type))); } catch (ClassNotFoundException | NoClassDefFoundError ex) { - resolved.add(ResolvableType.NONE); + resolved.add(new BeanType(className, ResolvableType.NONE)); } } return resolved; @@ -668,14 +668,14 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return "@" + ClassUtils.getShortName(this.annotationType); } - private Set deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) { + private Set deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) { if (metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) { return deducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata); } return Collections.emptySet(); } - private Set deducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata) { + private Set deducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata) { try { return Set.of(getReturnType(context, metadata)); } @@ -684,7 +684,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } } - private ResolvableType getReturnType(ConditionContext context, MethodMetadata metadata) + private BeanType 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(); @@ -692,12 +692,12 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat if (isParameterizedContainer(returnType.resolve())) { returnType = returnType.getGeneric(); } - return returnType; + return new BeanType(returnType.toString(), returnType); } private boolean isParameterizedContainer(@Nullable Class type) { return (type != null) && this.parameterizedContainers.stream() - .map(ResolvableType::resolve) + .map((beanType) -> beanType.resolvableType().resolve(type)) .anyMatch((container) -> container != null && container.isAssignableFrom(type)); } @@ -732,7 +732,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return (this.strategy != null) ? this.strategy : SearchStrategy.ALL; } - Set getTypes() { + Set getTypes() { return this.types; } @@ -748,11 +748,11 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return this.annotations; } - private Set getIgnoredTypes() { + private Set getIgnoredTypes() { return this.ignoredTypes; } - private Set getParameterizedContainers() { + private Set getParameterizedContainers() { return this.parameterizedContainers; } @@ -861,12 +861,12 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat this.unmatchedAnnotations.add(annotation); } - private void recordMatchedType(ResolvableType type, Collection matchingNames) { - this.matchedTypes.put(type.toString(), matchingNames); + private void recordMatchedType(BeanType type, Collection matchingNames) { + this.matchedTypes.put(type.name.toString(), matchingNames); this.namesOfAllMatches.addAll(matchingNames); } - private void recordUnmatchedType(ResolvableType type) { + private void recordUnmatchedType(BeanType type) { this.unmatchedTypes.add(type.toString()); } @@ -921,4 +921,13 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat } + record BeanType(String name, ResolvableType resolvableType) { + + @Override + public String toString() { + return ResolvableType.NONE.equals(this.resolvableType) ? this.name : this.resolvableType.toString(); + } + + } + } diff --git a/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java b/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java index 521d5807aef..05d4e9d2e32 100644 --- a/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java +++ b/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java @@ -35,6 +35,8 @@ import org.springframework.boot.autoconfigure.condition.scan.ScanBean; import org.springframework.boot.autoconfigure.condition.scan.ScannedFactoryBeanConfiguration; import org.springframework.boot.autoconfigure.condition.scan.ScannedFactoryBeanWithBeanMethodArgumentsConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener; +import org.springframework.boot.logging.LogLevel; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; @@ -503,6 +505,14 @@ class ConditionalOnMissingBeanTests { .satisfies(beansAndContainersNamed(GenericExampleBean.class, "customGenericExampleBean"))); } + @Test + void yep() { + this.contextRunner.withUserConfiguration(OnMissingBeanForTypeThatDoesNotExist.class) + .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) + .run((context) -> { + }); + } + private Consumer beansAndContainersNamed(Class type, String... names) { return (context) -> { String[] beans = context.getBeanNamesForType(type); @@ -1136,4 +1146,15 @@ class ConditionalOnMissingBeanTests { } + @Configuration(proxyBeanMethods = false) + static class OnMissingBeanForTypeThatDoesNotExist { + + @Bean + @ConditionalOnMissingBean(type = "com.example.DoesNotExist") + String bean() { + return "bean"; + } + + } + }