diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java index cb78bf95c7..22a0191d14 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java @@ -16,11 +16,15 @@ package org.springframework.context.annotation; +import java.lang.annotation.Annotation; import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,6 +35,8 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotation.Adapt; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.type.AnnotationMetadata; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -89,12 +95,15 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator { */ private static final Set conventionBasedStereotypeCheckCache = ConcurrentHashMap.newKeySet(); + private static final Adapt[] ADAPTATIONS = Adapt.values(false, true); + private final Log logger = LogFactory.getLog(AnnotationBeanNameGenerator.class); private final Map> metaAnnotationTypesCache = new ConcurrentHashMap<>(); + @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { if (definition instanceof AnnotatedBeanDefinition annotatedBeanDefinition) { @@ -122,24 +131,32 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator { return beanName; } - for (String annotationType : metadata.getAnnotationTypes()) { - AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, annotationType); - if (attributes != null) { - Set metaAnnotationTypes = this.metaAnnotationTypesCache.computeIfAbsent(annotationType, key -> { - Set result = metadata.getMetaAnnotationTypes(key); - return (result.isEmpty() ? Collections.emptySet() : result); - }); + // List of annotations directly present on the class we're searching on. + // MergedAnnotation implementations do not implement equals()/hashCode(), + // so we use a List and a 'visited' Set below. + List> mergedAnnotations = metadata.getAnnotations().stream() + .filter(MergedAnnotation::isDirectlyPresent) + .toList(); + + Set visited = new HashSet<>(); + + for (MergedAnnotation mergedAnnotation : mergedAnnotations) { + AnnotationAttributes attributes = mergedAnnotation.asAnnotationAttributes(ADAPTATIONS); + if (visited.add(attributes)) { + String annotationType = mergedAnnotation.getType().getName(); + Set metaAnnotationTypes = this.metaAnnotationTypesCache.computeIfAbsent(annotationType, + key -> getMetaAnnotationTypes(mergedAnnotation)); if (isStereotypeWithNameValue(annotationType, metaAnnotationTypes, attributes)) { Object value = attributes.get("value"); if (value instanceof String currentName && !currentName.isBlank()) { if (conventionBasedStereotypeCheckCache.add(annotationType) && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) && logger.isWarnEnabled()) { logger.warn(""" - Support for convention-based stereotype names is deprecated and will \ - be removed in a future version of the framework. Please annotate the \ - 'value' attribute in @%s with @AliasFor(annotation=Component.class) \ - to declare an explicit alias for @Component's 'value' attribute.""" - .formatted(annotationType)); + Support for convention-based stereotype names is deprecated and will \ + be removed in a future version of the framework. Please annotate the \ + 'value' attribute in @%s with @AliasFor(annotation=Component.class) \ + to declare an explicit alias for @Component's 'value' attribute.""" + .formatted(annotationType)); } if (beanName != null && !currentName.equals(beanName)) { throw new IllegalStateException("Stereotype annotations suggest inconsistent " + @@ -153,6 +170,13 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator { return beanName; } + private Set getMetaAnnotationTypes(MergedAnnotation mergedAnnotation) { + Set result = MergedAnnotations.from(mergedAnnotation.getType()).stream() + .map(metaAnnotation -> metaAnnotation.getType().getName()) + .collect(Collectors.toCollection(LinkedHashSet::new)); + return (result.isEmpty() ? Collections.emptySet() : result); + } + /** * Get the explicit bean name for the underlying class, as configured via * {@link org.springframework.stereotype.Component @Component} and taking into