From 800cbf2524c5548ddc259f62f30d1845801cd665 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 28 Mar 2019 13:44:35 -0700 Subject: [PATCH] Ignore 'value' attribute in AnnotationTypeMapping Update `addConventionAnnotationValues` so that `value` attributes do not override existing annotation values. The aligns the new `MergedAnnotations` API with the previous `AnnotatedElementUtils` logic. Closes gh-22703 --- .../annotation/AnnotationTypeMapping.java | 13 +++++-- .../AnnotatedElementUtilsTests.java | 36 +++++++++++++++++++ .../annotation/MergedAnnotationsTests.java | 33 +++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java index ee118b285e..b0f074321e 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java @@ -272,11 +272,11 @@ final class AnnotationTypeMapping { private void addConventionAnnotationValues() { for (int i = 0; i < this.attributes.size(); i++) { Method attribute = this.attributes.get(i); + boolean isValueAttribute = MergedAnnotation.VALUE.equals(attribute.getName()); AnnotationTypeMapping mapping = this; while (mapping != null && mapping.depth > 0) { int mapped = mapping.getAttributes().indexOf(attribute.getName()); - if (mapped != -1 && (this.annotationValueMappings[i] == -1 || - this.annotationValueSource[i].depth > mapping.depth)) { + if (mapped != -1 && isBetterConventionAnnotationValue(i, isValueAttribute, mapping)) { this.annotationValueMappings[i] = mapped; this.annotationValueSource[i] = mapping; } @@ -285,6 +285,15 @@ final class AnnotationTypeMapping { } } + private boolean isBetterConventionAnnotationValue(int index, boolean isValueAttribute, + AnnotationTypeMapping mapping) { + if (this.annotationValueMappings[index] == -1) { + return true; + } + int existingDepth = this.annotationValueSource[index].depth; + return !isValueAttribute && existingDepth > mapping.depth; + } + /** * Method called after all mappings have been set. At this point no further * lookups from child mappings will occur. diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java index e6273e4d21..f4704cfa0b 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java @@ -822,6 +822,14 @@ public class AnnotatedElementUtilsTests { assertThat(element.getDeclaredAnnotations()).isNotSameAs(element.getDeclaredAnnotations()); } + @Test // gh-22703 + public void getMergedAnnotationOnThreeDeepMetaWithValue() { + ValueAttribute annotation = AnnotatedElementUtils.getMergedAnnotation( + ValueAttributeMetaMetaClass.class, ValueAttribute.class); + assertThat(annotation.value()).containsExactly("FromValueAttributeMeta"); + } + + // ------------------------------------------------------------------------- @MetaCycle3 @@ -1394,4 +1402,32 @@ public class AnnotatedElementUtilsTests { class ForAnnotationsClass { } + @Retention(RetentionPolicy.RUNTIME) + static @interface ValueAttribute { + + String[] value(); + + } + + @Retention(RetentionPolicy.RUNTIME) + @ValueAttribute("FromValueAttributeMeta") + static @interface ValueAttributeMeta { + + @AliasFor("alias") + String[] value() default {}; + + @AliasFor("value") + String[] alias() default {}; + + } + + @Retention(RetentionPolicy.RUNTIME) + @ValueAttributeMeta("FromValueAttributeMetaMeta") + static @interface ValueAttributeMetaMeta { + } + + @ValueAttributeMetaMeta + static class ValueAttributeMetaMetaClass { + } + } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index d6b0a8b44d..d307ba125b 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -2045,6 +2045,14 @@ public class MergedAnnotationsTests { assertThat(annotation.getString("value")).isEqualTo("meta"); } + @Test // gh-22703 + public void getValueWhenThreeDeepMetaWithValue() { + MergedAnnotation annotation = MergedAnnotations.from( + ValueAttributeMetaMetaClass.class).get(ValueAttribute.class); + assertThat(annotation.getStringArray(MergedAnnotation.VALUE)).containsExactly( + "FromValueAttributeMeta"); + } + // @formatter:off @Retention(RetentionPolicy.RUNTIME) @@ -3430,6 +3438,31 @@ public class MergedAnnotationsTests { } + @Retention(RetentionPolicy.RUNTIME) + static @interface ValueAttribute { + + String[] value(); + + } + + @Retention(RetentionPolicy.RUNTIME) + @ValueAttribute("FromValueAttributeMeta") + static @interface ValueAttributeMeta { + + String[] value() default {}; + + } + + @Retention(RetentionPolicy.RUNTIME) + @ValueAttributeMeta("FromValueAttributeMetaMeta") + static @interface ValueAttributeMetaMeta { + + } + + @ValueAttributeMetaMeta + static class ValueAttributeMetaMetaClass { + + } // @formatter:on }