Skip to content

Commit b0a2f26

Browse files
committed
Merge branch '6.1.x'
2 parents 8de7c64 + 4baad16 commit b0a2f26

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotations.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -418,7 +418,10 @@ private MergedAnnotation<A> process(
418418

419419
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
420420
if (repeatedAnnotations != null) {
421-
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
421+
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
422+
if (result != null) {
423+
return result;
424+
}
422425
}
423426
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
424427
annotation.annotationType(), repeatableContainers, annotationFilter);

spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsRepeatableAnnotationTests.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.lang.annotation.RetentionPolicy;
2525
import java.lang.annotation.Target;
2626
import java.lang.reflect.AnnotatedElement;
27+
import java.util.Arrays;
2728
import java.util.Set;
2829
import java.util.stream.Stream;
2930

@@ -168,7 +169,7 @@ void typeHierarchyWhenOnClassReturnsAnnotations() {
168169
}
169170

170171
@Test
171-
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
172+
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
172173
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
173174
TYPE_HIERARCHY, SubRepeatableClass.class);
174175
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B", "C");
@@ -226,6 +227,44 @@ void typeHierarchyAnnotationsWithLocalComposedAnnotationWhoseRepeatableMetaAnnot
226227
assertThat(annotationTypes).containsExactly(WithRepeatedMetaAnnotations.class, Noninherited.class, Noninherited.class);
227228
}
228229

230+
@Test // gh-32731
231+
void searchFindsRepeatableContainerAnnotationAndRepeatedAnnotations() {
232+
Class<?> clazz = StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class;
233+
234+
// NO RepeatableContainers
235+
MergedAnnotations mergedAnnotations = MergedAnnotations.from(clazz, TYPE_HIERARCHY, RepeatableContainers.none());
236+
ContainerWithMultipleAttributes container = mergedAnnotations
237+
.get(ContainerWithMultipleAttributes.class)
238+
.synthesize(MergedAnnotation::isPresent).orElse(null);
239+
assertThat(container).as("container").isNotNull();
240+
assertThat(container.name()).isEqualTo("enigma");
241+
RepeatableWithContainerWithMultipleAttributes[] repeatedAnnotations = container.value();
242+
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
243+
.containsExactly("A", "B");
244+
Set<RepeatableWithContainerWithMultipleAttributes> set =
245+
mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
246+
.collect(MergedAnnotationCollectors.toAnnotationSet());
247+
// Only finds the locally declared repeated annotation.
248+
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
249+
.containsExactly("C");
250+
251+
// Standard RepeatableContainers
252+
mergedAnnotations = MergedAnnotations.from(clazz, TYPE_HIERARCHY, RepeatableContainers.standardRepeatables());
253+
container = mergedAnnotations
254+
.get(ContainerWithMultipleAttributes.class)
255+
.synthesize(MergedAnnotation::isPresent).orElse(null);
256+
assertThat(container).as("container").isNotNull();
257+
assertThat(container.name()).isEqualTo("enigma");
258+
repeatedAnnotations = container.value();
259+
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
260+
.containsExactly("A", "B");
261+
set = mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
262+
.collect(MergedAnnotationCollectors.toAnnotationSet());
263+
// Finds the locally declared repeated annotation plus the 2 in the container.
264+
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
265+
.containsExactly("A", "B", "C");
266+
}
267+
229268
private <A extends Annotation> Set<A> getAnnotations(Class<? extends Annotation> container,
230269
Class<A> repeatable, SearchStrategy searchStrategy, AnnotatedElement element) {
231270

@@ -420,4 +459,27 @@ static class SubNoninheritedRepeatableClass extends NoninheritedRepeatableClass
420459
static class WithRepeatedMetaAnnotationsClass {
421460
}
422461

462+
@Retention(RetentionPolicy.RUNTIME)
463+
@interface ContainerWithMultipleAttributes {
464+
465+
RepeatableWithContainerWithMultipleAttributes[] value();
466+
467+
String name() default "";
468+
}
469+
470+
@Retention(RetentionPolicy.RUNTIME)
471+
@Repeatable(ContainerWithMultipleAttributes.class)
472+
@interface RepeatableWithContainerWithMultipleAttributes {
473+
474+
String value() default "";
475+
}
476+
477+
@ContainerWithMultipleAttributes(name = "enigma", value = {
478+
@RepeatableWithContainerWithMultipleAttributes("A"),
479+
@RepeatableWithContainerWithMultipleAttributes("B")
480+
})
481+
@RepeatableWithContainerWithMultipleAttributes("C")
482+
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
483+
}
484+
423485
}

0 commit comments

Comments
 (0)