Use mapped annotation values when resolving mirrors

Update `TypeMappedAnnotation` mirror resolution logic so that mapped
annotation values are also considered. Prior to this commit, mirrors
in more complex meta-annotation scenarios would not resolve correctly.

Specifically, given the following:

 @interface TestAliased {

 	@AliasFor(attribute = "qualifier")
 	String value() default "";

 	@AliasFor(attribute = "value")
 	String qualifier() default "";
 }

 @TestAliased
 @interface TestMetaAliased {

 	@AliasFor(annotation = Aliased.class, attribute = "value")
 	String value() default "";
 }

 @TestMetaAliased("test")
 @interface TestComposed {
 }

A merged `@TestAliased` annotation obtained from a `@TestComposed`
root annotation would return a `value` and `qualifier` of "".

This was because the "value" and "qualifier" mirrors needed to be
resolved against the `@TestMetaAliased` meta-annotation. They cannot be
resolved against the declared `@TestComposed` annotation because it
does not have any attributes. Our previous tests only covered a single
depth scenario where `@TestMetaAliased` was used directly on the
annotated element.

Closes gh-23767
Co-authored-by: Sam Brannen <sbrannen@pivotal.io>
This commit is contained in:
Phillip Webb
2019-10-21 17:46:43 -07:00
parent b1a938fa24
commit ad543dbee3
4 changed files with 144 additions and 5 deletions

View File

@@ -444,15 +444,21 @@ final class AnnotationTypeMapping {
* convention and alias based mapping rules. For root mappings, this method
* will always return {@code null}.
* @param attributeIndex the attribute index of the source attribute
* @param metaAnnotationsOnly if only meta annotations should be considered.
* If this parameter is {@code false} then aliases within the annotation will
* not be excluded.
* @return the mapped annotation value, or {@code null}
*/
@Nullable
Object getMappedAnnotationValue(int attributeIndex) {
Object getMappedAnnotationValue(int attributeIndex, boolean metaAnnotationsOnly) {
int mapped = this.annotationValueMappings[attributeIndex];
if (mapped == -1) {
return null;
}
AnnotationTypeMapping source = this.annotationValueSource[attributeIndex];
if(source == this && metaAnnotationsOnly) {
return null;
}
return ReflectionUtils.invokeMethod(source.attributes.get(mapped), source.annotation);
}

View File

@@ -436,11 +436,15 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
private Object getValueFromMetaAnnotation(int attributeIndex,
boolean forMirrorResolution) {
if (this.useMergedValues && !forMirrorResolution) {
return this.mapping.getMappedAnnotationValue(attributeIndex);
Object value = null;
if (this.useMergedValues || forMirrorResolution) {
value = this.mapping.getMappedAnnotationValue(attributeIndex, forMirrorResolution);
}
Method attribute = this.mapping.getAttributes().get(attributeIndex);
return ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
if (value == null) {
Method attribute = mapping.getAttributes().get(attributeIndex);
value = ReflectionUtils.invokeMethod(attribute, mapping.getAnnotation());
}
return value;
}
@Nullable