Ensure all aliased attributes in target annotation are overridden

Prior to this commit, it was possible that implicit aliases and
transitive implicit aliases (configured via @AliasFor) might not be
honored in certain circumstances, in particular if implicit aliases
were declared to override different attributes within an alias pair in
the target meta-annotation.

This commit addresses this issue by ensuring that all aliased
attributes in the target meta-annotation are overridden during the
merge process in AnnotatedElementUtils.

In addition, concrete default values for attributes in a
meta-annotation declaration can now be effectively shadowed by
transitive implicit aliases in composed annotations.

Issue: SPR-14069
This commit is contained in:
Sam Brannen
2016-03-19 16:17:53 +01:00
parent 4cd7ba12bb
commit d22480b0eb
2 changed files with 60 additions and 25 deletions

View File

@@ -1041,15 +1041,36 @@ public class AnnotatedElementUtils {
annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
// Track which attribute values have already been replaced so that we can short
// circuit the search algorithms.
Set<String> valuesAlreadyReplaced = new HashSet<String>();
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
String attributeName = attributeMethod.getName();
String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
// Explicit annotation attribute override declared via @AliasFor
if (attributeOverrideName != null) {
if (attributes.containsKey(attributeOverrideName)) {
overrideAttribute(element, annotation, attributes, attributeName, attributeOverrideName);
if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
continue;
}
List<String> targetAttributeNames = new ArrayList<String>();
targetAttributeNames.add(attributeOverrideName);
valuesAlreadyReplaced.add(attributeOverrideName);
// Ensure all aliased attributes in the target annotation are also overridden. (SPR-14069)
List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
if (aliases != null) {
for (String alias : aliases) {
if (!valuesAlreadyReplaced.contains(alias)) {
targetAttributeNames.add(alias);
valuesAlreadyReplaced.add(alias);
}
}
}
overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
}
// Implicit annotation attribute override based on convention
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
@@ -1058,14 +1079,27 @@ public class AnnotatedElementUtils {
}
}
private void overrideAttribute(AnnotatedElement element, Annotation annotation,
AnnotationAttributes attributes, String sourceAttributeName, String targetAttributeName) {
private void overrideAttributes(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes,
String sourceAttributeName, List<String> targetAttributeNames) {
Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
Object adaptedValue = AnnotationUtils.adaptValue(
element, value, this.classValuesAsString, this.nestedAnnotationsAsMap);
Object adaptedValue = AnnotationUtils.adaptValue(element, value, this.classValuesAsString,
this.nestedAnnotationsAsMap);
for (String targetAttributeName : targetAttributeNames) {
attributes.put(targetAttributeName, adaptedValue);
}
}
private void overrideAttribute(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes,
String sourceAttributeName, String targetAttributeName) {
Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
Object adaptedValue = AnnotationUtils.adaptValue(element, value, this.classValuesAsString,
this.nestedAnnotationsAsMap);
attributes.put(targetAttributeName, adaptedValue);
}
}
}