Skip to content

Commit aa7864e

Browse files
Add ConditionalOnBean support for generic @bean return types
Update `ConditionalOnBean` so support generics when resolving the `@Bean` method. See gh-29500 Co-authored-by: Phillip Webb <phil.webb@broadcom.com>
1 parent fcad1fa commit aa7864e

File tree

3 files changed

+460
-60
lines changed

3 files changed

+460
-60
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java

+47-60
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
* @author Jakub Kubrynski
7777
* @author Stephane Nicoll
7878
* @author Andy Wilkinson
79+
* @author Uladzislau Seuruk
7980
* @see ConditionalOnBean
8081
* @see ConditionalOnMissingBean
8182
* @see ConditionalOnSingleCandidate
@@ -205,13 +206,13 @@ protected final MatchResult getMatchingBeans(Spec<?> spec) {
205206
ConfigurableListableBeanFactory beanFactory = getSearchBeanFactory(spec);
206207
ClassLoader classLoader = spec.getContext().getClassLoader();
207208
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
208-
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
209+
Set<ResolvableType> parameterizedContainers = spec.getParameterizedContainers();
209210
MatchResult result = new MatchResult();
210-
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
211+
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(beanFactory, considerHierarchy,
211212
spec.getIgnoredTypes(), parameterizedContainers);
212-
for (String type : spec.getTypes()) {
213-
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(classLoader,
214-
considerHierarchy, beanFactory, type, parameterizedContainers);
213+
for (ResolvableType type : spec.getTypes()) {
214+
Map<String, BeanDefinition> typeMatchedDefinitions = getBeanDefinitionsForType(beanFactory,
215+
considerHierarchy, type, parameterizedContainers);
215216
Set<String> typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions,
216217
(name, definition) -> !ScopedProxyUtils.isScopedTarget(name)
217218
&& isCandidate(beanFactory, name, definition, beansIgnoredByType));
@@ -296,42 +297,31 @@ private boolean isDefaultCandidate(BeanDefinition definition) {
296297
return true;
297298
}
298299

299-
private Set<String> getNamesOfBeansIgnoredByType(ClassLoader classLoader, ListableBeanFactory beanFactory,
300-
boolean considerHierarchy, Set<String> ignoredTypes, Set<Class<?>> parameterizedContainers) {
300+
private Set<String> getNamesOfBeansIgnoredByType(ListableBeanFactory beanFactory, boolean considerHierarchy,
301+
Set<ResolvableType> ignoredTypes, Set<ResolvableType> parameterizedContainers) {
301302
Set<String> result = null;
302-
for (String ignoredType : ignoredTypes) {
303-
Collection<String> ignoredNames = getBeanDefinitionsForType(classLoader, considerHierarchy, beanFactory,
304-
ignoredType, parameterizedContainers)
303+
for (ResolvableType ignoredType : ignoredTypes) {
304+
Collection<String> ignoredNames = getBeanDefinitionsForType(beanFactory, considerHierarchy, ignoredType,
305+
parameterizedContainers)
305306
.keySet();
306307
result = addAll(result, ignoredNames);
307308
}
308309
return (result != null) ? result : Collections.emptySet();
309310
}
310311

311-
private Map<String, BeanDefinition> getBeanDefinitionsForType(ClassLoader classLoader, boolean considerHierarchy,
312-
ListableBeanFactory beanFactory, String type, Set<Class<?>> parameterizedContainers) throws LinkageError {
313-
try {
314-
return getBeanDefinitionsForType(beanFactory, considerHierarchy, resolve(type, classLoader),
315-
parameterizedContainers);
316-
}
317-
catch (ClassNotFoundException | NoClassDefFoundError ex) {
318-
return Collections.emptyMap();
319-
}
320-
}
321-
322312
private Map<String, BeanDefinition> getBeanDefinitionsForType(ListableBeanFactory beanFactory,
323-
boolean considerHierarchy, Class<?> type, Set<Class<?>> parameterizedContainers) {
313+
boolean considerHierarchy, ResolvableType type, Set<ResolvableType> parameterizedContainers) {
324314
Map<String, BeanDefinition> result = collectBeanDefinitionsForType(beanFactory, considerHierarchy, type,
325315
parameterizedContainers, null);
326316
return (result != null) ? result : Collections.emptyMap();
327317
}
328318

329319
private Map<String, BeanDefinition> collectBeanDefinitionsForType(ListableBeanFactory beanFactory,
330-
boolean considerHierarchy, Class<?> type, Set<Class<?>> parameterizedContainers,
320+
boolean considerHierarchy, ResolvableType type, Set<ResolvableType> parameterizedContainers,
331321
Map<String, BeanDefinition> result) {
332322
result = putAll(result, beanFactory.getBeanNamesForType(type, true, false), beanFactory);
333-
for (Class<?> container : parameterizedContainers) {
334-
ResolvableType generic = ResolvableType.forClassWithGenerics(container, type);
323+
for (ResolvableType parameterizedContainer : parameterizedContainers) {
324+
ResolvableType generic = ResolvableType.forClassWithGenerics(parameterizedContainer.resolve(), type);
335325
result = putAll(result, beanFactory.getBeanNamesForType(generic, true, false), beanFactory);
336326
}
337327
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory hierarchicalBeanFactory) {
@@ -550,13 +540,13 @@ private static class Spec<A extends Annotation> {
550540

551541
private final Set<String> names;
552542

553-
private final Set<String> types;
543+
private final Set<ResolvableType> types;
554544

555545
private final Set<String> annotations;
556546

557-
private final Set<String> ignoredTypes;
547+
private final Set<ResolvableType> ignoredTypes;
558548

559-
private final Set<Class<?>> parameterizedContainers;
549+
private final Set<ResolvableType> parameterizedContainers;
560550

561551
private final SearchStrategy strategy;
562552

@@ -570,10 +560,10 @@ private static class Spec<A extends Annotation> {
570560
this.annotationType = annotationType;
571561
this.names = extract(attributes, "name");
572562
this.annotations = extract(attributes, "annotation");
573-
this.ignoredTypes = extract(attributes, "ignored", "ignoredType");
563+
this.ignoredTypes = resolveWhenPossible(extract(attributes, "ignored", "ignoredType"));
574564
this.parameterizedContainers = resolveWhenPossible(extract(attributes, "parameterizedContainer"));
575565
this.strategy = annotation.getValue("search", SearchStrategy.class).orElse(null);
576-
Set<String> types = extractTypes(attributes);
566+
Set<ResolvableType> types = resolveWhenPossible(extractTypes(attributes));
577567
BeanTypeDeductionException deductionException = null;
578568
if (types.isEmpty() && this.names.isEmpty() && this.annotations.isEmpty()) {
579569
try {
@@ -614,17 +604,18 @@ private void merge(Set<String> result, String... additional) {
614604
Collections.addAll(result, additional);
615605
}
616606

617-
private Set<Class<?>> resolveWhenPossible(Set<String> classNames) {
607+
private Set<ResolvableType> resolveWhenPossible(Set<String> classNames) {
618608
if (classNames.isEmpty()) {
619609
return Collections.emptySet();
620610
}
621-
Set<Class<?>> resolved = new LinkedHashSet<>(classNames.size());
611+
Set<ResolvableType> resolved = new LinkedHashSet<>(classNames.size());
622612
for (String className : classNames) {
623613
try {
624-
resolved.add(resolve(className, this.context.getClassLoader()));
614+
Class<?> type = resolve(className, this.context.getClassLoader());
615+
resolved.add(ResolvableType.forRawClass(type));
625616
}
626617
catch (ClassNotFoundException | NoClassDefFoundError ex) {
627-
// Ignore
618+
resolved.add(ResolvableType.NONE);
628619
}
629620
}
630621
return resolved;
@@ -653,48 +644,44 @@ protected final String getAnnotationName() {
653644
return "@" + ClassUtils.getShortName(this.annotationType);
654645
}
655646

656-
private Set<String> deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) {
647+
private Set<ResolvableType> deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) {
657648
if (metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) {
658649
return deducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata);
659650
}
660651
return Collections.emptySet();
661652
}
662653

663-
private Set<String> deducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata) {
654+
private Set<ResolvableType> deducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata) {
664655
try {
665-
Class<?> returnType = getReturnType(context, metadata);
666-
return Collections.singleton(returnType.getName());
656+
return Set.of(getReturnType(context, metadata));
667657
}
668658
catch (Throwable ex) {
669659
throw new BeanTypeDeductionException(metadata.getDeclaringClassName(), metadata.getMethodName(), ex);
670660
}
671661
}
672662

673-
private Class<?> getReturnType(ConditionContext context, MethodMetadata metadata)
663+
private ResolvableType getReturnType(ConditionContext context, MethodMetadata metadata)
674664
throws ClassNotFoundException, LinkageError {
675665
// Safe to load at this point since we are in the REGISTER_BEAN phase
676666
ClassLoader classLoader = context.getClassLoader();
677-
Class<?> returnType = resolve(metadata.getReturnTypeName(), classLoader);
678-
if (isParameterizedContainer(returnType)) {
679-
returnType = getReturnTypeGeneric(metadata, classLoader);
667+
ResolvableType returnType = getMethodReturnType(metadata, classLoader);
668+
if (isParameterizedContainer(returnType.resolve())) {
669+
returnType = returnType.getGeneric();
680670
}
681671
return returnType;
682672
}
683673

684674
private boolean isParameterizedContainer(Class<?> type) {
685-
for (Class<?> parameterizedContainer : this.parameterizedContainers) {
686-
if (parameterizedContainer.isAssignableFrom(type)) {
687-
return true;
688-
}
689-
}
690-
return false;
675+
return (type != null) && this.parameterizedContainers.stream()
676+
.map(ResolvableType::resolve)
677+
.anyMatch((container) -> container != null && container.isAssignableFrom(type));
691678
}
692679

693-
private Class<?> getReturnTypeGeneric(MethodMetadata metadata, ClassLoader classLoader)
680+
private ResolvableType getMethodReturnType(MethodMetadata metadata, ClassLoader classLoader)
694681
throws ClassNotFoundException, LinkageError {
695682
Class<?> declaringClass = resolve(metadata.getDeclaringClassName(), classLoader);
696683
Method beanMethod = findBeanMethod(declaringClass, metadata.getMethodName());
697-
return ResolvableType.forMethodReturnType(beanMethod).resolveGeneric();
684+
return ResolvableType.forMethodReturnType(beanMethod);
698685
}
699686

700687
private Method findBeanMethod(Class<?> declaringClass, String methodName) {
@@ -720,6 +707,10 @@ private SearchStrategy getStrategy() {
720707
return (this.strategy != null) ? this.strategy : SearchStrategy.ALL;
721708
}
722709

710+
Set<ResolvableType> getTypes() {
711+
return this.types;
712+
}
713+
723714
private ConditionContext getContext() {
724715
return this.context;
725716
}
@@ -728,19 +719,15 @@ private Set<String> getNames() {
728719
return this.names;
729720
}
730721

731-
protected Set<String> getTypes() {
732-
return this.types;
733-
}
734-
735722
private Set<String> getAnnotations() {
736723
return this.annotations;
737724
}
738725

739-
private Set<String> getIgnoredTypes() {
726+
private Set<ResolvableType> getIgnoredTypes() {
740727
return this.ignoredTypes;
741728
}
742729

743-
private Set<Class<?>> getParameterizedContainers() {
730+
private Set<ResolvableType> getParameterizedContainers() {
744731
return this.parameterizedContainers;
745732
}
746733

@@ -847,13 +834,13 @@ private void recordUnmatchedAnnotation(String annotation) {
847834
this.unmatchedAnnotations.add(annotation);
848835
}
849836

850-
private void recordMatchedType(String type, Collection<String> matchingNames) {
851-
this.matchedTypes.put(type, matchingNames);
837+
private void recordMatchedType(ResolvableType type, Collection<String> matchingNames) {
838+
this.matchedTypes.put(type.toString(), matchingNames);
852839
this.namesOfAllMatches.addAll(matchingNames);
853840
}
854841

855-
private void recordUnmatchedType(String type) {
856-
this.unmatchedTypes.add(type);
842+
private void recordUnmatchedType(ResolvableType type) {
843+
this.unmatchedTypes.add(type.toString());
857844
}
858845

859846
boolean isAllMatched() {

0 commit comments

Comments
 (0)