Find all @ComponentScan and @PropertySource annotations

Prior to this commit, Spring failed to find multiple composed
@ComponentScan and @PropertySource annotations or multiple
@ComponentScans and @PropertySources container annotations. The reason
was due to lacking support in the AnnotatedTypeMetadata API.

This commit introduces support for finding all @ComponentScan and
@PropertySource annotations by making use of the new
getMergedRepeatableAnnotationAttributes() method in
AnnotatedTypeMetadata.

Closes gh-30941
See gh-31041
This commit is contained in:
Sam Brannen
2023-08-06 12:02:08 +03:00
parent 0b902f32f6
commit 3bda2b7124
8 changed files with 89 additions and 37 deletions

View File

@@ -17,9 +17,7 @@
package org.springframework.context.annotation;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
@@ -51,6 +49,7 @@ import org.springframework.util.ClassUtils;
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 2.5
* @see ContextAnnotationAutowireCandidateResolver
* @see ConfigurationClassPostProcessor
@@ -281,33 +280,10 @@ public abstract class AnnotationConfigUtils {
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationTypeName));
}
@SuppressWarnings("unchecked")
static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata,
Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
Class<? extends Annotation> annotationType, Class<? extends Annotation> containerType) {
Set<AnnotationAttributes> result = new LinkedHashSet<>();
// Direct annotation present or meta-present?
addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationType.getName()));
// Container annotation present or meta-present?
Map<String, Object> container = metadata.getAnnotationAttributes(containerType.getName());
if (container != null && container.containsKey("value")) {
for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) {
addAttributesIfNotNull(result, containedAttributes);
}
}
// Return merged result
return Collections.unmodifiableSet(result);
}
private static void addAttributesIfNotNull(
Set<AnnotationAttributes> result, @Nullable Map<String, Object> attributes) {
if (attributes != null) {
result.add(AnnotationAttributes.fromMap(attributes));
}
return metadata.getMergedRepeatableAnnotationAttributes(annotationType, containerType, false);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 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.
@@ -46,6 +46,10 @@ import org.springframework.core.type.filter.TypeFilter;
*
* <p>See {@link Configuration @Configuration}'s Javadoc for usage examples.
*
* <p>{@code @ComponentScan} can be used as a <em>{@linkplain Repeatable repeatable}</em>
* annotation. {@code @ComponentScan} may also be used as a <em>meta-annotation</em>
* to create custom <em>composed annotations</em> with attribute overrides.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen

View File

@@ -267,8 +267,8 @@ class ConfigurationClassParser {
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
sourceClass.getMetadata(), org.springframework.context.annotation.PropertySource.class,
PropertySources.class)) {
if (this.propertySourceRegistry != null) {
this.propertySourceRegistry.processPropertySource(propertySource);
}
@@ -280,7 +280,7 @@ class ConfigurationClassParser {
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {

View File

@@ -147,11 +147,9 @@ import org.springframework.core.io.support.PropertySourceFactory;
* ConfigurableEnvironment} and {@link org.springframework.core.env.MutablePropertySources
* MutablePropertySources} javadocs for details.
*
* <p><b>NOTE: This annotation is repeatable according to Java 8 conventions.</b>
* However, all such {@code @PropertySource} annotations need to be declared at the same
* level: either directly on the configuration class or as meta-annotations on the
* same custom annotation. Mixing direct annotations and meta-annotations is not
* recommended since direct annotations will effectively override meta-annotations.
* <p>{@code @PropertySource} can be used as a <em>{@linkplain Repeatable repeatable}</em>
* annotation. {@code @PropertySource} may also be used as a <em>meta-annotation</em>
* to create custom <em>composed annotations</em> with attribute overrides.
*
* @author Chris Beams
* @author Juergen Hoeller