@ -21,6 +21,7 @@ import java.lang.annotation.Annotation;
@@ -21,6 +21,7 @@ import java.lang.annotation.Annotation;
import java.lang.annotation.Documented ;
import java.lang.annotation.ElementType ;
import java.lang.annotation.Inherited ;
import java.lang.annotation.Repeatable ;
import java.lang.annotation.Retention ;
import java.lang.annotation.RetentionPolicy ;
import java.lang.annotation.Target ;
@ -32,6 +33,7 @@ import java.util.Set;
@@ -32,6 +33,7 @@ import java.util.Set;
import org.junit.jupiter.api.Test ;
import org.springframework.core.annotation.AliasFor ;
import org.springframework.core.annotation.AnnotatedElementUtils ;
import org.springframework.core.annotation.AnnotationAttributes ;
import org.springframework.core.testfixture.stereotype.Component ;
import org.springframework.core.type.classreading.MetadataReader ;
@ -247,6 +249,82 @@ class AnnotationMetadataTests {
@@ -247,6 +249,82 @@ class AnnotationMetadataTests {
assertMultipleAnnotationsWithIdenticalAttributeNames ( metadata ) ;
}
@Test // gh-31041
void multipleComposedRepeatableAnnotationsUsingStandardAnnotationMetadata ( ) {
AnnotationMetadata metadata = AnnotationMetadata . introspect ( MultipleComposedRepeatableAnnotationsClass . class ) ;
assertRepeatableAnnotations ( metadata ) ;
}
@Test // gh-31041
void multipleComposedRepeatableAnnotationsUsingSimpleAnnotationMetadata ( ) throws Exception {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory ( ) ;
MetadataReader metadataReader = metadataReaderFactory . getMetadataReader ( MultipleComposedRepeatableAnnotationsClass . class . getName ( ) ) ;
AnnotationMetadata metadata = metadataReader . getAnnotationMetadata ( ) ;
assertRepeatableAnnotations ( metadata ) ;
}
@Test // gh-31041
void multipleRepeatableAnnotationsInContainersUsingStandardAnnotationMetadata ( ) {
AnnotationMetadata metadata = AnnotationMetadata . introspect ( MultipleRepeatableAnnotationsInContainersClass . class ) ;
assertRepeatableAnnotations ( metadata ) ;
}
@Test // gh-31041
void multipleRepeatableAnnotationsInContainersUsingSimpleAnnotationMetadata ( ) throws Exception {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory ( ) ;
MetadataReader metadataReader = metadataReaderFactory . getMetadataReader ( MultipleRepeatableAnnotationsInContainersClass . class . getName ( ) ) ;
AnnotationMetadata metadata = metadataReader . getAnnotationMetadata ( ) ;
assertRepeatableAnnotations ( metadata ) ;
}
/ * *
* Tests { @code AnnotatedElementUtils # getMergedRepeatableAnnotations ( ) } variants to ensure that
* { @link AnnotationMetadata # getMergedRepeatableAnnotationAttributes ( Class , Class , boolean ) }
* behaves the same .
* /
@Test // gh-31041
void multipleComposedRepeatableAnnotationsUsingAnnotatedElementUtils ( ) throws Exception {
Class < ? > element = MultipleComposedRepeatableAnnotationsClass . class ;
Set < TestComponentScan > annotations = AnnotatedElementUtils . getMergedRepeatableAnnotations ( element , TestComponentScan . class ) ;
assertRepeatableAnnotations ( annotations ) ;
annotations = AnnotatedElementUtils . getMergedRepeatableAnnotations ( element , TestComponentScan . class , TestComponentScans . class ) ;
assertRepeatableAnnotations ( annotations ) ;
}
/ * *
* Tests { @code AnnotatedElementUtils # getMergedRepeatableAnnotations ( ) } variants to ensure that
* { @link AnnotationMetadata # getMergedRepeatableAnnotationAttributes ( Class , Class , boolean ) }
* behaves the same .
* /
@Test // gh-31041
void multipleRepeatableAnnotationsInContainersUsingAnnotatedElementUtils ( ) throws Exception {
Class < ? > element = MultipleRepeatableAnnotationsInContainersClass . class ;
Set < TestComponentScan > annotations = AnnotatedElementUtils . getMergedRepeatableAnnotations ( element , TestComponentScan . class ) ;
assertRepeatableAnnotations ( annotations ) ;
annotations = AnnotatedElementUtils . getMergedRepeatableAnnotations ( element , TestComponentScan . class , TestComponentScans . class ) ;
assertRepeatableAnnotations ( annotations ) ;
}
private static void assertRepeatableAnnotations ( AnnotationMetadata metadata ) {
Set < AnnotationAttributes > attributesSet =
metadata . getMergedRepeatableAnnotationAttributes ( TestComponentScan . class , TestComponentScans . class , false ) ;
assertThat ( attributesSet . stream ( ) . map ( attributes - > attributes . getStringArray ( "value" ) ) . flatMap ( Arrays : : stream ) )
. containsExactly ( "A" , "B" , "C" , "D" ) ;
assertThat ( attributesSet . stream ( ) . map ( attributes - > attributes . getStringArray ( "basePackages" ) ) . flatMap ( Arrays : : stream ) )
. containsExactly ( "A" , "B" , "C" , "D" ) ;
}
private static void assertRepeatableAnnotations ( Set < TestComponentScan > annotations ) {
assertThat ( annotations . stream ( ) . map ( TestComponentScan : : value ) . flatMap ( Arrays : : stream ) )
. containsExactly ( "A" , "B" , "C" , "D" ) ;
assertThat ( annotations . stream ( ) . map ( TestComponentScan : : basePackages ) . flatMap ( Arrays : : stream ) )
. containsExactly ( "A" , "B" , "C" , "D" ) ;
}
@Test
void inheritedAnnotationWithMetaAnnotationsWithIdenticalAttributeNamesUsingStandardAnnotationMetadata ( ) {
AnnotationMetadata metadata = AnnotationMetadata . introspect ( NamedComposedAnnotationExtended . class ) ;
@ -534,6 +612,14 @@ class AnnotationMetadataTests {
@@ -534,6 +612,14 @@ class AnnotationMetadataTests {
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
public @interface TestComponentScans {
TestComponentScan [ ] value ( ) ;
}
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
@Repeatable ( TestComponentScans . class )
public @interface TestComponentScan {
@AliasFor ( "basePackages" )
@ -560,6 +646,40 @@ class AnnotationMetadataTests {
@@ -560,6 +646,40 @@ class AnnotationMetadataTests {
public static class ComposedConfigurationWithAttributeOverridesClass {
}
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
@TestComponentScan ( "C" )
public @interface ScanPackageC {
}
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
@TestComponentScan ( "D" )
public @interface ScanPackageD {
}
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
@TestComponentScans ( {
@TestComponentScan ( "C" ) ,
@TestComponentScan ( "D" )
} )
public @interface ScanPackagesCandD {
}
@TestComponentScan ( "A" )
@ScanPackageC
@ScanPackageD
@TestComponentScan ( "B" )
static class MultipleComposedRepeatableAnnotationsClass {
}
@TestComponentScan ( "A" )
@ScanPackagesCandD
@TestComponentScans ( @TestComponentScan ( "B" ) )
static class MultipleRepeatableAnnotationsInContainersClass {
}
@Retention ( RetentionPolicy . RUNTIME )
@Target ( ElementType . TYPE )
public @interface NamedAnnotation1 {