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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user