Browse Source

Document getAttributeAliasMap() in AnnotationUtils

This commit also introduces a cache for attribute alias metadata.

Issue: SPR-11512
pull/808/head
Sam Brannen 10 years ago
parent
commit
62d1b4b6e8
  1. 95
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

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

@ -118,6 +118,9 @@ public abstract class AnnotationUtils {
private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache = private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache =
new ConcurrentReferenceHashMap<Class<? extends Annotation>, Boolean>(256); new ConcurrentReferenceHashMap<Class<? extends Annotation>, Boolean>(256);
private static final Map<Class<? extends Annotation>, Map<String, String>> attributeAliasCache =
new ConcurrentReferenceHashMap<Class<? extends Annotation>, Map<String, String>>(256);
private static transient Log logger; private static transient Log logger;
@ -1058,20 +1061,38 @@ public abstract class AnnotationUtils {
return annotation; return annotation;
} }
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(annotatedElement, annotation, getAliasMap(annotationType)); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(annotatedElement, annotation,
getAttributeAliasMap(annotationType));
A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] { A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] {
(Class<A>) annotationType, SynthesizedAnnotation.class }, handler); (Class<A>) annotationType, SynthesizedAnnotation.class }, handler);
return synthesizedAnnotation; return synthesizedAnnotation;
} }
/** /**
* TODO Document getAliasMap(). * Get a map of all attribute alias pairs, declared via {@code @AliasFor}
* in the supplied annotation type.
*
* <p>The map is keyed by attribute name with each value representing
* the name of the aliased attribute. For each entry {@code [x, y]} in
* the map there will be a corresponding {@code [y, x]} entry in the map.
*
* <p>An empty return value implies that the annotation does not declare
* any attribute aliases.
*
* @param annotationType the annotation type to find attribute aliases in
* @return a map containing attribute alias pairs; never {@code null}
* @since 4.2 * @since 4.2
*/ */
private static Map<String, String> getAliasMap(Class<? extends Annotation> annotationType) { private static Map<String, String> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
if (annotationType == null) { if (annotationType == null) {
return null; return Collections.emptyMap();
}
Map<String, String> cachedMap = attributeAliasCache.get(annotationType);
if (cachedMap != null) {
return cachedMap;
} }
Map<String, String> map = new HashMap<String, String>(); Map<String, String> map = new HashMap<String, String>();
@ -1082,6 +1103,9 @@ public abstract class AnnotationUtils {
map.put(attributeName, aliasedAttributeName); map.put(attributeName, aliasedAttributeName);
} }
} }
attributeAliasCache.put(annotationType, map);
return map; return map;
} }
@ -1283,9 +1307,9 @@ public abstract class AnnotationUtils {
/** /**
* TODO Document postProcessAnnotationAttributes(). * TODO Document postProcessAnnotationAttributes().
* *
* @param annotatedElement the element that is annotated with the supplied * @param element the element that is annotated with the supplied annotation,
* annotation, used for contextual logging; may be {@code null} if unknown * used for contextual logging; may be {@code null} if unknown
* @param attributes the annotation attributes to validate * @param attributes the annotation attributes to post-process
* @since 4.2 * @since 4.2
*/ */
static void postProcessAnnotationAttributes(AnnotatedElement element, AnnotationAttributes attributes, static void postProcessAnnotationAttributes(AnnotatedElement element, AnnotationAttributes attributes,
@ -1297,39 +1321,36 @@ public abstract class AnnotationUtils {
} }
Class<? extends Annotation> annotationType = attributes.annotationType(); Class<? extends Annotation> annotationType = attributes.annotationType();
Map<String, String> aliasMap = getAliasMap(annotationType);
// Validate @AliasFor configuration // Validate @AliasFor configuration
if (aliasMap != null) { Map<String, String> aliasMap = getAttributeAliasMap(annotationType);
Set<String> validated = new HashSet<String>(); Set<String> validated = new HashSet<String>();
for (String attributeName : aliasMap.keySet()) {
for (String attributeName : aliasMap.keySet()) { String aliasedAttributeName = aliasMap.get(attributeName);
String aliasedAttributeName = aliasMap.get(attributeName);
if (validated.add(attributeName) && validated.add(aliasedAttributeName)) {
if (validated.add(attributeName) && validated.add(aliasedAttributeName)) { Object value = attributes.get(attributeName);
Object value = attributes.get(attributeName); Object aliasedValue = attributes.get(aliasedAttributeName);
Object aliasedValue = attributes.get(aliasedAttributeName);
if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !DEFAULT_VALUE_PLACEHOLDER.equals(value)
if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !DEFAULT_VALUE_PLACEHOLDER.equals(value) && !DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
&& !DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) { String elementAsString = (element == null ? "unknown element" : element.toString());
String elementAsString = (element == null ? "unknown element" : element.toString()); String msg = String.format(
String msg = String.format( "In AnnotationAttributes for annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are "
"In AnnotationAttributes for annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are " + "declared with values of [%s] and [%s], but only one declaration is permitted.",
+ "declared with values of [%s] and [%s], but only one declaration is permitted.", annotationType.getName(), elementAsString, attributeName, aliasedAttributeName,
annotationType.getName(), elementAsString, attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue));
ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue)); throw new AnnotationConfigurationException(msg);
throw new AnnotationConfigurationException(msg); }
}
// Replace default values with aliased values... // Replace default values with aliased values...
if (DEFAULT_VALUE_PLACEHOLDER.equals(value)) { if (DEFAULT_VALUE_PLACEHOLDER.equals(value)) {
attributes.put(attributeName, attributes.put(attributeName,
adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap)); adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
} }
if (DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) { if (DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
attributes.put(aliasedAttributeName, attributes.put(aliasedAttributeName,
adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap)); adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
}
} }
} }
} }

Loading…
Cancel
Save