Skip to content

Commit 9876701

Browse files
committed
Support nesting in AnnotatedElementUtils.getMergedRepeatableAnnotations()
This commit is a follow up to 828f74f and applies to same fix for getMergedRepeatableAnnotations(). See the previous commit for details. Closes gh-20279
1 parent 828f74f commit 9876701

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,23 @@ private static MergedAnnotations getAnnotations(AnnotatedElement element) {
766766
private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element,
767767
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
768768

769-
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
769+
RepeatableContainers repeatableContainers;
770+
if (containerType == null) {
771+
// Invoke RepeatableContainers.of() in order to adhere to the contract of
772+
// getMergedRepeatableAnnotations() which states that an IllegalArgumentException
773+
// will be thrown if the the container cannot be resolved.
774+
//
775+
// In any case, we use standardRepeatables() in order to support repeatable
776+
// annotations on other types of repeatable annotations (i.e., nested repeatable
777+
// annotation types).
778+
//
779+
// See https://github.com/spring-projects/spring-framework/issues/20279
780+
RepeatableContainers.of(annotationType, null);
781+
repeatableContainers = RepeatableContainers.standardRepeatables();
782+
}
783+
else {
784+
repeatableContainers = RepeatableContainers.of(annotationType, containerType);
785+
}
770786
return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, repeatableContainers);
771787
}
772788

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ void findMergedRepeatableAnnotations_AnnotatedElementUtils() {
6464
assertThat(annotations).extracting(A::value).containsExactly(5);
6565
}
6666

67+
@Test
68+
void getMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
69+
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class);
70+
// Merged, so we expect to find @A once with its value coming from @B(5).
71+
assertThat(annotations).extracting(A::value).containsExactly(5);
72+
}
73+
74+
@Test
75+
void getMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
76+
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class, A.Container.class);
77+
// Merged, so we expect to find @A once with its value coming from @B(5).
78+
assertThat(annotations).extracting(A::value).containsExactly(5);
79+
}
80+
6781
@Test
6882
@SuppressWarnings("deprecation")
6983
void getRepeatableAnnotations_AnnotationUtils() {
@@ -107,7 +121,6 @@ void streamRepeatableAnnotationsWithExplicitRepeatables_MergedAnnotationsApi() {
107121
void findMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
108122
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class);
109123
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
110-
// However, findMergedRepeatableAnnotations() currently finds ZERO annotations.
111124
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
112125
}
113126

@@ -126,6 +139,28 @@ void findMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils(
126139
assertThat(annotations).isEmpty();
127140
}
128141

142+
@Test
143+
void getMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
144+
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class);
145+
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
146+
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
147+
}
148+
149+
@Test
150+
void getMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
151+
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class, A.Container.class);
152+
// When getMergedRepeatableAnnotations(...) is invoked with an explicit container
153+
// type, it uses RepeatableContainers.of(...) which limits the repeatable annotation
154+
// support to a single container type.
155+
//
156+
// In this test case, we are therefore limiting the support to @A.Container, which
157+
// means that @B.Container is unsupported and effectively ignored as a repeatable
158+
// container type.
159+
//
160+
// Long story, short: the search doesn't find anything.
161+
assertThat(annotations).isEmpty();
162+
}
163+
129164
@Test
130165
@SuppressWarnings("deprecation")
131166
void getRepeatableAnnotations_AnnotationUtils() {

0 commit comments

Comments
 (0)