From 79ed0a75c72ed537fac601a57f4ddc185e65d6c7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 19 Mar 2021 15:25:48 +0100 Subject: [PATCH] Support @Value meta-annotations and expose MergedAnnotations on PreferredConstructor parameters. We now support composed annotation that are annotated with `@Value`. we also expose MergedAnnotations through PreferredConstructor.Parameter for further use by store modules that want to inspect constructor argument annotations. Closes: #2332 Original Pull Request: #2333 --- .../data/mapping/PreferredConstructor.java | 25 ++++++++++++---- ...eferredConstructorDiscovererUnitTests.java | 30 +++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/mapping/PreferredConstructor.java b/src/main/java/org/springframework/data/mapping/PreferredConstructor.java index a779c6a2a..69eaa09f5 100644 --- a/src/main/java/org/springframework/data/mapping/PreferredConstructor.java +++ b/src/main/java/org/springframework/data/mapping/PreferredConstructor.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.MergedAnnotations; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.util.Lazy; import org.springframework.data.util.TypeInformation; @@ -175,6 +176,7 @@ public class PreferredConstructor> { private final @Nullable String name; private final TypeInformation type; + private final MergedAnnotations annotations; private final String key; private final @Nullable PersistentEntity entity; @@ -199,7 +201,8 @@ public class PreferredConstructor> { this.name = name; this.type = type; - this.key = getValue(annotations); + this.annotations = MergedAnnotations.from(annotations); + this.key = getValue(this.annotations); this.entity = entity; this.enclosingClassCache = Lazy.of(() -> { @@ -216,12 +219,12 @@ public class PreferredConstructor> { } @Nullable - private static String getValue(Annotation[] annotations) { + private static String getValue(MergedAnnotations annotations) { - return Arrays.stream(annotations)// - .filter(it -> it.annotationType() == Value.class)// - .findFirst().map(it -> ((Value) it).value())// - .filter(StringUtils::hasText).orElse(null); + return annotations.get(Value.class) // + .getValue("value", String.class) // + .filter(StringUtils::hasText) // + .orElse(null); } /** @@ -243,6 +246,16 @@ public class PreferredConstructor> { return type; } + /** + * Merged annotations that this parameter is annotated with. + * + * @return + * @since 2.5 + */ + public MergedAnnotations getAnnotations() { + return annotations; + } + /** * Returns the raw resolved type of the parameter. * diff --git a/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java b/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java index 031f91604..297791f9d 100755 --- a/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/PreferredConstructorDiscovererUnitTests.java @@ -17,9 +17,15 @@ package org.springframework.data.mapping; import static org.assertj.core.api.Assertions.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Iterator; import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.mapping.PreferredConstructorDiscovererUnitTests.Outer.Inner; @@ -135,6 +141,16 @@ class PreferredConstructorDiscovererUnitTests

> { }); } + @Test // GH-2332 + void detectsMetaAnnotatedValueAnnotation() { + + assertThat(PreferredConstructorDiscoverer.discover(ClassWithMetaAnnotatedParameter.class)).satisfies(ctor -> { + + assertThat(ctor.getParameters().get(0).getSpelExpression()).isEqualTo("${hello-world}"); + assertThat(ctor.getParameters().get(0).getAnnotations()).isNotNull(); + }); + } + static class SyntheticConstructor { @PersistenceConstructor private SyntheticConstructor(String x) {} @@ -204,4 +220,18 @@ class PreferredConstructorDiscovererUnitTests

> { super(value); } } + + static class ClassWithMetaAnnotatedParameter { + + ClassWithMetaAnnotatedParameter(@MyValue String value) { + + } + } + + @Target({ ElementType.PARAMETER, }) + @Retention(RetentionPolicy.RUNTIME) + @Value("${hello-world}") + @interface MyValue { + + } }