From ada4498968f95ad83f7c50717f44b5a70b06568b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 26 Jan 2017 15:00:05 +0100 Subject: [PATCH] DATACMNS-1273 - Consider merged annotations when populating for annotation cache. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now consider property annotations as merged annotations when initially scanning for annotations in a property declaration. Composed annotations such as declared outside our code are captured correctly because they use an own type that is not queried by users of PersistentProperty. Own, revised annotations, using @AliasFor providing an alias for annotation values require merged annotation processing. Previously, annotations were cached as-is without resolving @AliasFor. This caused a later lookup via findAnnotation(…) to return the cached annotation without aliasing. Because the annotation was cached, no further lookup via AnnotatedElementUtils.findMergedAnnotation(…) was attempted. @Retention(RetentionPolicy.RUNTIME) @Target(value = { FIELD, METHOD }) public @interface RevisedAnnnotationWithAliasFor { @AliasFor("value") String name() default ""; @AliasFor("name") String value() default ""; } public class Person { @RevisedAnnnotationWithAliasFor(value = "spring") String firstname; } PersistentProperty firstname = … property.findAnnotation(…) returned previously @RevisedAnnnotationWithAliasFor(value = "spring", name = "") now we return @RevisedAnnnotationWithAliasFor(value = "spring", name = "spring") Original ticket: DATACMNS-981. Java 6 compatible backport of 2da5acc55354a246e3c08fc93b8be0ccc80854ec. --- .../AnnotationBasedPersistentProperty.java | 7 ++-- ...ationBasedPersistentPropertyUnitTests.java | 37 +++++++++++++++---- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java b/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java index e41782663d..f8168a7cde 100644 --- a/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java +++ b/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2016 the original author or authors. + * Copyright 2011-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ * * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch */ public abstract class AnnotationBasedPersistentProperty

> extends AbstractPersistentProperty

{ @@ -99,7 +100,7 @@ private final void populateAnnotationCache(Field field) { + "multiple times on accessor methods of property %s in class %s!", annotationType.getSimpleName(), getName(), getOwner().getType().getSimpleName()); - cacheAndReturn(annotationType, annotation); + cacheAndReturn(annotationType, AnnotatedElementUtils.findMergedAnnotation(method, annotationType)); } } @@ -115,7 +116,7 @@ private final void populateAnnotationCache(Field field) { "Ambiguous mapping! Annotation %s configured " + "on field %s and one of its accessor methods in class %s!", annotationType.getSimpleName(), field.getName(), getOwner().getType().getSimpleName()); - cacheAndReturn(annotationType, annotation); + cacheAndReturn(annotationType, AnnotatedElementUtils.findMergedAnnotation(field, annotationType)); } } diff --git a/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java index bd354af6e4..6001215ab4 100644 --- a/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Test; + import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.annotation.AccessType; @@ -46,6 +47,7 @@ * * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch */ public class AnnotationBasedPersistentPropertyUnitTests

> { @@ -197,6 +199,16 @@ public void composedAnnotationWithAliasShouldHaveSynthesizedAttributeValues() { assertThat(AnnotationUtils.getValue(annotation), is((Object) "spring")); } + @Test // DATACMNS-867, DATACMNS-981, DATACMNS-1273 + public void revisedAnnotationWithAliasShouldHaveSynthesizedAttributeValues() { + + SamplePersistentProperty setter = entity.getPersistentProperty("setter"); + RevisedAnnnotationWithAliasFor annotation = setter.findAnnotation(RevisedAnnnotationWithAliasFor.class); + + assertThat(annotation.name(), is(equalTo("my-value"))); + assertThat(annotation.value(), is(equalTo("my-value"))); + } + @SuppressWarnings("unchecked") private Map, Annotation> getAnnotationCache(SamplePersistentProperty property) { return (Map, Annotation>) ReflectionTestUtils.getField(property, "annotationCache"); @@ -220,7 +232,7 @@ static class Sample { @MyAnnotation String field; String getter; - String setter; + @RevisedAnnnotationWithAliasFor(value = "my-value") String setter; String doubleMapping; @MyAnnotationAsMeta String meta; @@ -282,30 +294,41 @@ public String getProperty() { @Retention(RetentionPolicy.RUNTIME) @Target(value = { FIELD, METHOD, ANNOTATION_TYPE }) - public static @interface MyAnnotation { + public @interface MyAnnotation { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(value = { FIELD, METHOD }) @MyAnnotation - public static @interface MyAnnotationAsMeta { + public @interface MyAnnotationAsMeta { } @Retention(RetentionPolicy.RUNTIME) @Target(value = { FIELD, METHOD }) @MyAnnotation - public static @interface MyComposedAnnotationUsingAliasFor { + public @interface MyComposedAnnotationUsingAliasFor { @AliasFor(annotation = MyAnnotation.class, attribute = "value") String name() default "spring"; } + @Retention(RetentionPolicy.RUNTIME) + @Target(value = { FIELD, METHOD }) + @interface RevisedAnnnotationWithAliasFor { + + @AliasFor("value") + String name() default ""; + + @AliasFor("name") + String value() default ""; + } + @Retention(RetentionPolicy.RUNTIME) @Target(value = { FIELD, METHOD, ANNOTATION_TYPE }) @Id - public static @interface MyId { + public @interface MyId { } static class FieldAccess { @@ -352,7 +375,7 @@ public String getField() { @ReadOnlyProperty @Retention(RetentionPolicy.RUNTIME) @Target(FIELD) - static @interface CustomReadOnly { + @interface CustomReadOnly { } }