DATACMNS-1273 - Consider merged annotations when populating for annotation cache.

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 2da5acc553.
This commit is contained in:
Mark Paluch
2017-01-26 15:00:05 +01:00
parent aadeebbb7d
commit ada4498968
2 changed files with 34 additions and 10 deletions

View File

@@ -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 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>>
extends AbstractPersistentProperty<P> {
@@ -99,7 +100,7 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
+ "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 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
"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));
}
}

View File

@@ -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 javax.annotation.Nullable;
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 @@ import org.springframework.test.util.ReflectionTestUtils;
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBasedPersistentProperty<P>> {
@@ -197,6 +199,16 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
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<Class<? extends Annotation>, Annotation> getAnnotationCache(SamplePersistentProperty property) {
return (Map<Class<? extends Annotation>, Annotation>) ReflectionTestUtils.getField(property, "annotationCache");
@@ -220,7 +232,7 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
@MyAnnotation String field;
String getter;
String setter;
@RevisedAnnnotationWithAliasFor(value = "my-value") String setter;
String doubleMapping;
@MyAnnotationAsMeta String meta;
@@ -282,30 +294,41 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
@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 class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
@ReadOnlyProperty
@Retention(RetentionPolicy.RUNTIME)
@Target(FIELD)
static @interface CustomReadOnly {
@interface CustomReadOnly {
}
}