Skip to content

Commit 62d1b4b

Browse files
committed
Document getAttributeAliasMap() in AnnotationUtils
This commit also introduces a cache for attribute alias metadata. Issue: SPR-11512
1 parent 92bf32b commit 62d1b4b

File tree

1 file changed

+58
-37
lines changed

1 file changed

+58
-37
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ public abstract class AnnotationUtils {
118118
private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache =
119119
new ConcurrentReferenceHashMap<Class<? extends Annotation>, Boolean>(256);
120120

121+
private static final Map<Class<? extends Annotation>, Map<String, String>> attributeAliasCache =
122+
new ConcurrentReferenceHashMap<Class<? extends Annotation>, Map<String, String>>(256);
123+
121124
private static transient Log logger;
122125

123126

@@ -1058,20 +1061,38 @@ public static <A extends Annotation> A synthesizeAnnotation(A annotation, Annota
10581061
return annotation;
10591062
}
10601063

1061-
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(annotatedElement, annotation, getAliasMap(annotationType));
1064+
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(annotatedElement, annotation,
1065+
getAttributeAliasMap(annotationType));
10621066
A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] {
10631067
(Class<A>) annotationType, SynthesizedAnnotation.class }, handler);
10641068

10651069
return synthesizedAnnotation;
10661070
}
10671071

1072+
10681073
/**
1069-
* TODO Document getAliasMap().
1074+
* Get a map of all attribute alias pairs, declared via {@code @AliasFor}
1075+
* in the supplied annotation type.
1076+
*
1077+
* <p>The map is keyed by attribute name with each value representing
1078+
* the name of the aliased attribute. For each entry {@code [x, y]} in
1079+
* the map there will be a corresponding {@code [y, x]} entry in the map.
1080+
*
1081+
* <p>An empty return value implies that the annotation does not declare
1082+
* any attribute aliases.
1083+
*
1084+
* @param annotationType the annotation type to find attribute aliases in
1085+
* @return a map containing attribute alias pairs; never {@code null}
10701086
* @since 4.2
10711087
*/
1072-
private static Map<String, String> getAliasMap(Class<? extends Annotation> annotationType) {
1088+
private static Map<String, String> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
10731089
if (annotationType == null) {
1074-
return null;
1090+
return Collections.emptyMap();
1091+
}
1092+
1093+
Map<String, String> cachedMap = attributeAliasCache.get(annotationType);
1094+
if (cachedMap != null) {
1095+
return cachedMap;
10751096
}
10761097

10771098
Map<String, String> map = new HashMap<String, String>();
@@ -1082,6 +1103,9 @@ private static Map<String, String> getAliasMap(Class<? extends Annotation> annot
10821103
map.put(attributeName, aliasedAttributeName);
10831104
}
10841105
}
1106+
1107+
attributeAliasCache.put(annotationType, map);
1108+
10851109
return map;
10861110
}
10871111

@@ -1283,9 +1307,9 @@ static List<Method> getAttributeMethods(Class<? extends Annotation> annotationTy
12831307
/**
12841308
* TODO Document postProcessAnnotationAttributes().
12851309
*
1286-
* @param annotatedElement the element that is annotated with the supplied
1287-
* annotation, used for contextual logging; may be {@code null} if unknown
1288-
* @param attributes the annotation attributes to validate
1310+
* @param element the element that is annotated with the supplied annotation,
1311+
* used for contextual logging; may be {@code null} if unknown
1312+
* @param attributes the annotation attributes to post-process
12891313
* @since 4.2
12901314
*/
12911315
static void postProcessAnnotationAttributes(AnnotatedElement element, AnnotationAttributes attributes,
@@ -1297,39 +1321,36 @@ static void postProcessAnnotationAttributes(AnnotatedElement element, Annotation
12971321
}
12981322

12991323
Class<? extends Annotation> annotationType = attributes.annotationType();
1300-
Map<String, String> aliasMap = getAliasMap(annotationType);
13011324

13021325
// Validate @AliasFor configuration
1303-
if (aliasMap != null) {
1304-
Set<String> validated = new HashSet<String>();
1305-
1306-
for (String attributeName : aliasMap.keySet()) {
1307-
String aliasedAttributeName = aliasMap.get(attributeName);
1308-
1309-
if (validated.add(attributeName) && validated.add(aliasedAttributeName)) {
1310-
Object value = attributes.get(attributeName);
1311-
Object aliasedValue = attributes.get(aliasedAttributeName);
1312-
1313-
if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !DEFAULT_VALUE_PLACEHOLDER.equals(value)
1314-
&& !DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
1315-
String elementAsString = (element == null ? "unknown element" : element.toString());
1316-
String msg = String.format(
1317-
"In AnnotationAttributes for annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are "
1318-
+ "declared with values of [%s] and [%s], but only one declaration is permitted.",
1319-
annotationType.getName(), elementAsString, attributeName, aliasedAttributeName,
1320-
ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue));
1321-
throw new AnnotationConfigurationException(msg);
1322-
}
1326+
Map<String, String> aliasMap = getAttributeAliasMap(annotationType);
1327+
Set<String> validated = new HashSet<String>();
1328+
for (String attributeName : aliasMap.keySet()) {
1329+
String aliasedAttributeName = aliasMap.get(attributeName);
1330+
1331+
if (validated.add(attributeName) && validated.add(aliasedAttributeName)) {
1332+
Object value = attributes.get(attributeName);
1333+
Object aliasedValue = attributes.get(aliasedAttributeName);
1334+
1335+
if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !DEFAULT_VALUE_PLACEHOLDER.equals(value)
1336+
&& !DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
1337+
String elementAsString = (element == null ? "unknown element" : element.toString());
1338+
String msg = String.format(
1339+
"In AnnotationAttributes for annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are "
1340+
+ "declared with values of [%s] and [%s], but only one declaration is permitted.",
1341+
annotationType.getName(), elementAsString, attributeName, aliasedAttributeName,
1342+
ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue));
1343+
throw new AnnotationConfigurationException(msg);
1344+
}
13231345

1324-
// Replace default values with aliased values...
1325-
if (DEFAULT_VALUE_PLACEHOLDER.equals(value)) {
1326-
attributes.put(attributeName,
1327-
adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
1328-
}
1329-
if (DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
1330-
attributes.put(aliasedAttributeName,
1331-
adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
1332-
}
1346+
// Replace default values with aliased values...
1347+
if (DEFAULT_VALUE_PLACEHOLDER.equals(value)) {
1348+
attributes.put(attributeName,
1349+
adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
1350+
}
1351+
if (DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
1352+
attributes.put(aliasedAttributeName,
1353+
adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
13331354
}
13341355
}
13351356
}

0 commit comments

Comments
 (0)