Consistent alias processing behind AnnotatedTypeMetadata abstraction (also for ASM)
Issue: SPR-14427
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,13 +35,13 @@ import org.springframework.util.ObjectUtils;
|
||||
* @param <S> the type of source supported by this extractor
|
||||
* @see Annotation
|
||||
* @see AliasFor
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, Object)
|
||||
*/
|
||||
abstract class AbstractAliasAwareAnnotationAttributeExtractor<S> implements AnnotationAttributeExtractor<S> {
|
||||
|
||||
private final Class<? extends Annotation> annotationType;
|
||||
|
||||
private final AnnotatedElement annotatedElement;
|
||||
private final Object annotatedElement;
|
||||
|
||||
private final S source;
|
||||
|
||||
@@ -56,7 +56,7 @@ abstract class AbstractAliasAwareAnnotationAttributeExtractor<S> implements Anno
|
||||
* @param source the underlying source of annotation attributes; never {@code null}
|
||||
*/
|
||||
AbstractAliasAwareAnnotationAttributeExtractor(
|
||||
Class<? extends Annotation> annotationType, AnnotatedElement annotatedElement, S source) {
|
||||
Class<? extends Annotation> annotationType, Object annotatedElement, S source) {
|
||||
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
Assert.notNull(source, "source must not be null");
|
||||
@@ -73,7 +73,7 @@ abstract class AbstractAliasAwareAnnotationAttributeExtractor<S> implements Anno
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AnnotatedElement getAnnotatedElement() {
|
||||
public final Object getAnnotatedElement() {
|
||||
return this.annotatedElement;
|
||||
}
|
||||
|
||||
@@ -89,18 +89,18 @@ abstract class AbstractAliasAwareAnnotationAttributeExtractor<S> implements Anno
|
||||
|
||||
List<String> aliasNames = this.attributeAliasMap.get(attributeName);
|
||||
if (aliasNames != null) {
|
||||
Object defaultValue = AnnotationUtils.getDefaultValue(getAnnotationType(), attributeName);
|
||||
Object defaultValue = AnnotationUtils.getDefaultValue(this.annotationType, attributeName);
|
||||
for (String aliasName : aliasNames) {
|
||||
Object aliasValue = getRawAttributeValue(aliasName);
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(attributeValue, aliasValue) &&
|
||||
!ObjectUtils.nullSafeEquals(attributeValue, defaultValue) &&
|
||||
!ObjectUtils.nullSafeEquals(aliasValue, defaultValue)) {
|
||||
String elementName = (getAnnotatedElement() != null ? getAnnotatedElement().toString() : "unknown element");
|
||||
String elementName = (this.annotatedElement != null ? this.annotatedElement.toString() : "unknown element");
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"In annotation [%s] declared on %s and synthesized from [%s], attribute '%s' and its " +
|
||||
"alias '%s' are present with values of [%s] and [%s], but only one is permitted.",
|
||||
getAnnotationType().getName(), elementName, getSource(), attributeName, aliasName,
|
||||
this.annotationType.getName(), elementName, this.source, attributeName, aliasName,
|
||||
ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue)));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
@@ -44,7 +43,7 @@ interface AnnotationAttributeExtractor<S> {
|
||||
* type supported by this extractor.
|
||||
* @return the annotated element, or {@code null} if unknown
|
||||
*/
|
||||
AnnotatedElement getAnnotatedElement();
|
||||
Object getAnnotatedElement();
|
||||
|
||||
/**
|
||||
* Get the underlying source of annotation attributes.
|
||||
|
||||
@@ -50,30 +50,21 @@ import org.springframework.util.StringUtils;
|
||||
@SuppressWarnings("serial")
|
||||
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
|
||||
private final Class<? extends Annotation> annotationType;
|
||||
private static final String UNKNOWN = "unknown";
|
||||
|
||||
private Class<? extends Annotation> annotationType;
|
||||
|
||||
private final String displayName;
|
||||
|
||||
boolean validated = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new, empty {@link AnnotationAttributes} instance.
|
||||
*/
|
||||
public AnnotationAttributes() {
|
||||
this.annotationType = null;
|
||||
this.displayName = "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new, empty {@link AnnotationAttributes} instance for the
|
||||
* specified {@code annotationType}.
|
||||
* @param annotationType the type of annotation represented by this
|
||||
* {@code AnnotationAttributes} instance; never {@code null}
|
||||
* @since 4.2
|
||||
*/
|
||||
public AnnotationAttributes(Class<? extends Annotation> annotationType) {
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
this.annotationType = annotationType;
|
||||
this.displayName = annotationType.getName();
|
||||
this.displayName = UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,20 +75,68 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
public AnnotationAttributes(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
this.annotationType = null;
|
||||
this.displayName = "unknown";
|
||||
this.displayName = UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationAttributes} instance, wrapping the
|
||||
* provided map and all its <em>key-value</em> pairs.
|
||||
* @param map original source of annotation attribute <em>key-value</em>
|
||||
* pairs
|
||||
* Create a new, empty {@link AnnotationAttributes} instance for the
|
||||
* specified {@code annotationType}.
|
||||
* @param annotationType the type of annotation represented by this
|
||||
* {@code AnnotationAttributes} instance; never {@code null}
|
||||
* @since 4.2
|
||||
*/
|
||||
public AnnotationAttributes(Class<? extends Annotation> annotationType) {
|
||||
Assert.notNull(annotationType, "'annotationType' must not be null");
|
||||
this.annotationType = annotationType;
|
||||
this.displayName = annotationType.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new, empty {@link AnnotationAttributes} instance for the
|
||||
* specified {@code annotationType}.
|
||||
* @param annotationType the type of annotation represented by this
|
||||
* {@code AnnotationAttributes} instance; never {@code null}
|
||||
* @param classLoader the ClassLoader to try to load the annotation type on,
|
||||
* or {@code null} to just store the annotation type name
|
||||
* @since 4.3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AnnotationAttributes(String annotationType, ClassLoader classLoader) {
|
||||
Assert.notNull(annotationType, "'annotationType' must not be null");
|
||||
if (classLoader != null) {
|
||||
try {
|
||||
this.annotationType = (Class<? extends Annotation>) classLoader.loadClass(annotationType);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Annotation Class not resolvable
|
||||
}
|
||||
}
|
||||
this.displayName = annotationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationAttributes} instance, wrapping the provided
|
||||
* map and all its <em>key-value</em> pairs.
|
||||
* @param map original source of annotation attribute <em>key-value</em> pairs
|
||||
* @see #fromMap(Map)
|
||||
*/
|
||||
public AnnotationAttributes(Map<String, Object> map) {
|
||||
super(map);
|
||||
this.annotationType = null;
|
||||
this.displayName = "unknown";
|
||||
this.displayName = UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationAttributes} instance, wrapping the provided
|
||||
* map and all its <em>key-value</em> pairs.
|
||||
* @param other original source of annotation attribute <em>key-value</em> pairs
|
||||
* @see #fromMap(Map)
|
||||
*/
|
||||
public AnnotationAttributes(AnnotationAttributes other) {
|
||||
super(other);
|
||||
this.annotationType = other.annotationType;
|
||||
this.displayName = other.displayName;
|
||||
this.validated = other.validated;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,34 +163,6 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
return getRequiredAttribute(attributeName, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value stored under the specified {@code attributeName} as a
|
||||
* string, taking into account alias semantics defined via
|
||||
* {@link AliasFor @AliasFor}.
|
||||
* <p>If there is no value stored under the specified {@code attributeName}
|
||||
* but the attribute has an alias declared via {@code @AliasFor}, the
|
||||
* value of the alias will be returned.
|
||||
* @param attributeName the name of the attribute to get; never
|
||||
* {@code null} or empty
|
||||
* @param annotationType the type of annotation represented by this
|
||||
* {@code AnnotationAttributes} instance; never {@code null}
|
||||
* @param annotationSource the source of the annotation represented by
|
||||
* this {@code AnnotationAttributes} (e.g., the {@link AnnotatedElement});
|
||||
* or {@code null} if unknown
|
||||
* @return the string value
|
||||
* @throws IllegalArgumentException if the attribute and its alias do
|
||||
* not exist or are not of type {@code String}
|
||||
* @throws AnnotationConfigurationException if the attribute and its
|
||||
* alias are both present with different non-empty values
|
||||
* @since 4.2
|
||||
* @see ObjectUtils#isEmpty(Object)
|
||||
*/
|
||||
public String getAliasedString(String attributeName, Class<? extends Annotation> annotationType,
|
||||
Object annotationSource) {
|
||||
|
||||
return getRequiredAttributeWithAlias(attributeName, annotationType, annotationSource, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value stored under the specified {@code attributeName} as an
|
||||
* array of strings.
|
||||
@@ -168,33 +179,6 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
return getRequiredAttribute(attributeName, String[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value stored under the specified {@code attributeName} as an
|
||||
* array of strings, taking into account alias semantics defined via
|
||||
* {@link AliasFor @AliasFor}.
|
||||
* <p>If there is no value stored under the specified {@code attributeName}
|
||||
* but the attribute has an alias declared via {@code @AliasFor}, the
|
||||
* value of the alias will be returned.
|
||||
* @param attributeName the name of the attribute to get; never
|
||||
* {@code null} or empty
|
||||
* @param annotationType the type of annotation represented by this
|
||||
* {@code AnnotationAttributes} instance; never {@code null}
|
||||
* @param annotationSource the source of the annotation represented by
|
||||
* this {@code AnnotationAttributes} (e.g., the {@link AnnotatedElement});
|
||||
* or {@code null} if unknown
|
||||
* @return the array of strings
|
||||
* @throws IllegalArgumentException if the attribute and its alias do
|
||||
* not exist or are not of type {@code String[]}
|
||||
* @throws AnnotationConfigurationException if the attribute and its
|
||||
* alias are both present with different non-empty values
|
||||
* @since 4.2
|
||||
*/
|
||||
public String[] getAliasedStringArray(String attributeName, Class<? extends Annotation> annotationType,
|
||||
Object annotationSource) {
|
||||
|
||||
return getRequiredAttributeWithAlias(attributeName, annotationType, annotationSource, String[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value stored under the specified {@code attributeName} as a
|
||||
* boolean.
|
||||
@@ -266,33 +250,6 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
return getRequiredAttribute(attributeName, Class[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value stored under the specified {@code attributeName} as an
|
||||
* array of classes, taking into account alias semantics defined via
|
||||
* {@link AliasFor @AliasFor}.
|
||||
* <p>If there is no value stored under the specified {@code attributeName}
|
||||
* but the attribute has an alias declared via {@code @AliasFor}, the
|
||||
* value of the alias will be returned.
|
||||
* @param attributeName the name of the attribute to get; never
|
||||
* {@code null} or empty
|
||||
* @param annotationType the type of annotation represented by this
|
||||
* {@code AnnotationAttributes} instance; never {@code null}
|
||||
* @param annotationSource the source of the annotation represented by
|
||||
* this {@code AnnotationAttributes} (e.g., the {@link AnnotatedElement});
|
||||
* or {@code null} if unknown
|
||||
* @return the array of classes
|
||||
* @throws IllegalArgumentException if the attribute and its alias do
|
||||
* not exist or are not of type {@code Class[]}
|
||||
* @throws AnnotationConfigurationException if the attribute and its
|
||||
* alias are both present with different non-empty values
|
||||
* @since 4.2
|
||||
*/
|
||||
public Class<?>[] getAliasedClassArray(String attributeName, Class<? extends Annotation> annotationType,
|
||||
Object annotationSource) {
|
||||
|
||||
return getRequiredAttributeWithAlias(attributeName, annotationType, annotationSource, Class[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link AnnotationAttributes} stored under the specified
|
||||
* {@code attributeName}.
|
||||
@@ -378,7 +335,7 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
|
||||
Assert.hasText(attributeName, "attributeName must not be null or empty");
|
||||
Assert.hasText(attributeName, "'attributeName' must not be null or empty");
|
||||
Object value = get(attributeName);
|
||||
assertAttributePresence(attributeName, value);
|
||||
assertNotException(attributeName, value);
|
||||
@@ -418,9 +375,9 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
private <T> T getRequiredAttributeWithAlias(String attributeName, Class<? extends Annotation> annotationType,
|
||||
Object annotationSource, Class<T> expectedType) {
|
||||
|
||||
Assert.hasText(attributeName, "attributeName must not be null or empty");
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
Assert.notNull(expectedType, "expectedType must not be null");
|
||||
Assert.hasText(attributeName, "'attributeName' must not be null or empty");
|
||||
Assert.notNull(annotationType, "'annotationType' must not be null");
|
||||
Assert.notNull(expectedType, "'expectedType' must not be null");
|
||||
|
||||
T attributeValue = getAttribute(attributeName, expectedType);
|
||||
|
||||
@@ -433,8 +390,8 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
|
||||
if (!attributeEmpty && !aliasEmpty && !ObjectUtils.nullSafeEquals(attributeValue, aliasValue)) {
|
||||
String elementName = (annotationSource == null ? "unknown element" : annotationSource.toString());
|
||||
String msg = String.format("In annotation [%s] declared on [%s], attribute [%s] and its alias [%s] " +
|
||||
"are present with values of [%s] and [%s], but only one is permitted.",
|
||||
String msg = String.format("In annotation [%s] declared on [%s], attribute [%s] and its " +
|
||||
"alias [%s] are present with values of [%s] and [%s], but only one is permitted.",
|
||||
annotationType.getName(), elementName, attributeName, aliasName,
|
||||
ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue));
|
||||
throw new AnnotationConfigurationException(msg);
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -590,7 +591,7 @@ public abstract class AnnotationUtils {
|
||||
static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
|
||||
Boolean found = annotatedInterfaceCache.get(iface);
|
||||
if (found != null) {
|
||||
return found.booleanValue();
|
||||
return found;
|
||||
}
|
||||
found = Boolean.FALSE;
|
||||
for (Method ifcMethod : iface.getMethods()) {
|
||||
@@ -605,7 +606,7 @@ public abstract class AnnotationUtils {
|
||||
}
|
||||
}
|
||||
annotatedInterfaceCache.put(iface, found);
|
||||
return found.booleanValue();
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -857,14 +858,14 @@ public abstract class AnnotationUtils {
|
||||
AnnotationCacheKey cacheKey = new AnnotationCacheKey(annotationType, metaAnnotationType);
|
||||
Boolean metaPresent = metaPresentCache.get(cacheKey);
|
||||
if (metaPresent != null) {
|
||||
return metaPresent.booleanValue();
|
||||
return metaPresent;
|
||||
}
|
||||
metaPresent = Boolean.FALSE;
|
||||
if (findAnnotation(annotationType, metaAnnotationType, false) != null) {
|
||||
metaPresent = Boolean.TRUE;
|
||||
}
|
||||
metaPresentCache.put(cacheKey, metaPresent);
|
||||
return metaPresent.booleanValue();
|
||||
return metaPresent;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -987,6 +988,13 @@ public abstract class AnnotationUtils {
|
||||
public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement,
|
||||
Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
return getAnnotationAttributes(
|
||||
(Object) annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
private static AnnotationAttributes getAnnotationAttributes(Object annotatedElement,
|
||||
Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
AnnotationAttributes attributes =
|
||||
retrieveAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap);
|
||||
postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, nestedAnnotationsAsMap);
|
||||
@@ -1021,7 +1029,7 @@ public abstract class AnnotationUtils {
|
||||
* @since 4.2
|
||||
* @see #postProcessAnnotationAttributes
|
||||
*/
|
||||
static AnnotationAttributes retrieveAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation,
|
||||
static AnnotationAttributes retrieveAnnotationAttributes(Object annotatedElement, Annotation annotation,
|
||||
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
@@ -1065,14 +1073,14 @@ public abstract class AnnotationUtils {
|
||||
* {@code Annotation} instances
|
||||
* @return the adapted value, or the original value if no adaptation is needed
|
||||
*/
|
||||
static Object adaptValue(AnnotatedElement annotatedElement, Object value, boolean classValuesAsString,
|
||||
static Object adaptValue(Object annotatedElement, Object value, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
|
||||
if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
if (value instanceof Class<?>) {
|
||||
return ((Class<?>) value).getName();
|
||||
}
|
||||
else if (value instanceof Class[]) {
|
||||
else if (value instanceof Class<?>[]) {
|
||||
Class<?>[] clazzArray = (Class<?>[]) value;
|
||||
String[] classNames = new String[clazzArray.length];
|
||||
for (int i = 0; i < clazzArray.length; i++) {
|
||||
@@ -1111,6 +1119,64 @@ public abstract class AnnotationUtils {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the annotation-declared default values for the given attributes,
|
||||
* if available.
|
||||
* @param attributes the annotation attributes to process
|
||||
* @since 4.3.2
|
||||
*/
|
||||
public static void registerDefaultValues(AnnotationAttributes attributes) {
|
||||
// Only do defaults scanning for public annotations; we'd run into
|
||||
// IllegalAccessExceptions otherwise, and we don't want to mess with
|
||||
// accessibility in a SecurityManager environment.
|
||||
Class<?> annotationType = attributes.annotationType();
|
||||
if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) {
|
||||
// Check declared default values of attributes in the annotation type.
|
||||
Method[] annotationAttributes = annotationType.getMethods();
|
||||
for (Method annotationAttribute : annotationAttributes) {
|
||||
String attributeName = annotationAttribute.getName();
|
||||
Object defaultValue = annotationAttribute.getDefaultValue();
|
||||
if (defaultValue != null && !attributes.containsKey(attributeName)) {
|
||||
if (defaultValue instanceof Annotation) {
|
||||
defaultValue = getAnnotationAttributes((Annotation) defaultValue, false, true);
|
||||
}
|
||||
else if (defaultValue instanceof Annotation[]) {
|
||||
Annotation[] realAnnotations = (Annotation[]) defaultValue;
|
||||
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
||||
for (int i = 0; i < realAnnotations.length; i++) {
|
||||
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], false, true);
|
||||
}
|
||||
defaultValue = mappedAnnotations;
|
||||
}
|
||||
attributes.put(attributeName, new DefaultValueHolder(defaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the supplied {@link AnnotationAttributes}, preserving nested
|
||||
* annotations as {@code Annotation} instances.
|
||||
* <p>Specifically, this method enforces <em>attribute alias</em> semantics
|
||||
* for annotation attributes that are annotated with {@link AliasFor @AliasFor}
|
||||
* and replaces default value placeholders with their original default values.
|
||||
* @param annotatedElement the element that is annotated with an annotation or
|
||||
* annotation hierarchy from which the supplied attributes were created;
|
||||
* may be {@code null} if unknown
|
||||
* @param attributes the annotation attributes to post-process
|
||||
* @param classValuesAsString whether to convert Class references into Strings (for
|
||||
* compatibility with {@link org.springframework.core.type.AnnotationMetadata})
|
||||
* or to preserve them as Class references
|
||||
* @since 4.3.2
|
||||
* @see #postProcessAnnotationAttributes(Object, AnnotationAttributes, boolean, boolean)
|
||||
* @see #getDefaultValue(Class, String)
|
||||
*/
|
||||
public static void postProcessAnnotationAttributes(Object annotatedElement,
|
||||
AnnotationAttributes attributes, boolean classValuesAsString) {
|
||||
|
||||
postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the supplied {@link AnnotationAttributes}.
|
||||
* <p>Specifically, this method enforces <em>attribute alias</em> semantics
|
||||
@@ -1128,10 +1194,10 @@ public abstract class AnnotationUtils {
|
||||
* {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
|
||||
* {@code Annotation} instances
|
||||
* @since 4.2
|
||||
* @see #retrieveAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
|
||||
* @see #retrieveAnnotationAttributes(Object, Annotation, boolean, boolean)
|
||||
* @see #getDefaultValue(Class, String)
|
||||
*/
|
||||
static void postProcessAnnotationAttributes(AnnotatedElement annotatedElement,
|
||||
static void postProcessAnnotationAttributes(Object annotatedElement,
|
||||
AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
|
||||
// Abort?
|
||||
@@ -1145,51 +1211,55 @@ public abstract class AnnotationUtils {
|
||||
// circuit the search algorithms.
|
||||
Set<String> valuesAlreadyReplaced = new HashSet<>();
|
||||
|
||||
// Validate @AliasFor configuration
|
||||
Map<String, List<String>> aliasMap = getAttributeAliasMap(annotationType);
|
||||
for (String attributeName : aliasMap.keySet()) {
|
||||
if (valuesAlreadyReplaced.contains(attributeName)) {
|
||||
continue;
|
||||
}
|
||||
Object value = attributes.get(attributeName);
|
||||
boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder));
|
||||
|
||||
for (String aliasedAttributeName : aliasMap.get(attributeName)) {
|
||||
if (valuesAlreadyReplaced.contains(aliasedAttributeName)) {
|
||||
if (!attributes.validated) {
|
||||
// Validate @AliasFor configuration
|
||||
Map<String, List<String>> aliasMap = getAttributeAliasMap(annotationType);
|
||||
for (String attributeName : aliasMap.keySet()) {
|
||||
if (valuesAlreadyReplaced.contains(attributeName)) {
|
||||
continue;
|
||||
}
|
||||
Object value = attributes.get(attributeName);
|
||||
boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder));
|
||||
|
||||
Object aliasedValue = attributes.get(aliasedAttributeName);
|
||||
boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder));
|
||||
for (String aliasedAttributeName : aliasMap.get(attributeName)) {
|
||||
if (valuesAlreadyReplaced.contains(aliasedAttributeName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Something to validate or replace with an alias?
|
||||
if (valuePresent || aliasPresent) {
|
||||
if (valuePresent && aliasPresent) {
|
||||
// Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals().
|
||||
if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) {
|
||||
String elementAsString = (annotatedElement != null ? annotatedElement.toString() : "unknown element");
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"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 is permitted.", annotationType.getName(), elementAsString,
|
||||
attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value),
|
||||
ObjectUtils.nullSafeToString(aliasedValue)));
|
||||
Object aliasedValue = attributes.get(aliasedAttributeName);
|
||||
boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder));
|
||||
|
||||
// Something to validate or replace with an alias?
|
||||
if (valuePresent || aliasPresent) {
|
||||
if (valuePresent && aliasPresent) {
|
||||
// Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals().
|
||||
if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) {
|
||||
String elementAsString =
|
||||
(annotatedElement != null ? annotatedElement.toString() : "unknown element");
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"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 is permitted.", annotationType.getName(), elementAsString,
|
||||
attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value),
|
||||
ObjectUtils.nullSafeToString(aliasedValue)));
|
||||
}
|
||||
}
|
||||
else if (aliasPresent) {
|
||||
// Replace value with aliasedValue
|
||||
attributes.put(attributeName,
|
||||
adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
|
||||
valuesAlreadyReplaced.add(attributeName);
|
||||
}
|
||||
else {
|
||||
// Replace aliasedValue with value
|
||||
attributes.put(aliasedAttributeName,
|
||||
adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
|
||||
valuesAlreadyReplaced.add(aliasedAttributeName);
|
||||
}
|
||||
}
|
||||
else if (aliasPresent) {
|
||||
// Replace value with aliasedValue
|
||||
attributes.put(attributeName,
|
||||
adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
|
||||
valuesAlreadyReplaced.add(attributeName);
|
||||
}
|
||||
else {
|
||||
// Replace aliasedValue with value
|
||||
attributes.put(aliasedAttributeName,
|
||||
adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
|
||||
valuesAlreadyReplaced.add(aliasedAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
attributes.validated = true;
|
||||
}
|
||||
|
||||
// Replace any remaining placeholders with actual default values
|
||||
@@ -1329,8 +1399,12 @@ public abstract class AnnotationUtils {
|
||||
* @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
* @see #synthesizeAnnotation(Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) {
|
||||
return synthesizeAnnotation(annotation, (Object) annotatedElement);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <A extends Annotation> A synthesizeAnnotation(A annotation, Object annotatedElement) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1435,7 +1509,7 @@ public abstract class AnnotationUtils {
|
||||
* @see #synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
* @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
*/
|
||||
public static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) {
|
||||
static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Object annotatedElement) {
|
||||
if (annotations == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1463,7 +1537,7 @@ public abstract class AnnotationUtils {
|
||||
* {@code @AliasFor} is detected
|
||||
* @since 4.2.1
|
||||
* @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
|
||||
* @see #synthesizeAnnotationArray(Annotation[], AnnotatedElement)
|
||||
* @see #synthesizeAnnotationArray(Annotation[], Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <A extends Annotation> A[] synthesizeAnnotationArray(Map<String, Object>[] maps, Class<A> annotationType) {
|
||||
@@ -1551,7 +1625,7 @@ public abstract class AnnotationUtils {
|
||||
private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {
|
||||
Boolean synthesizable = synthesizableCache.get(annotationType);
|
||||
if (synthesizable != null) {
|
||||
return synthesizable.booleanValue();
|
||||
return synthesizable;
|
||||
}
|
||||
|
||||
synthesizable = Boolean.FALSE;
|
||||
@@ -1579,7 +1653,7 @@ public abstract class AnnotationUtils {
|
||||
}
|
||||
|
||||
synthesizableCache.put(annotationType, synthesizable);
|
||||
return synthesizable.booleanValue();
|
||||
return synthesizable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -32,7 +31,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @see AliasFor
|
||||
* @see AbstractAliasAwareAnnotationAttributeExtractor
|
||||
* @see MapAnnotationAttributeExtractor
|
||||
* @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||
* @see AnnotationUtils#synthesizeAnnotation
|
||||
*/
|
||||
class DefaultAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttributeExtractor<Annotation> {
|
||||
|
||||
@@ -42,7 +41,7 @@ class DefaultAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAt
|
||||
* @param annotatedElement the element that is annotated with the supplied
|
||||
* annotation; may be {@code null} if unknown
|
||||
*/
|
||||
DefaultAnnotationAttributeExtractor(Annotation annotation, AnnotatedElement annotatedElement) {
|
||||
DefaultAnnotationAttributeExtractor(Annotation annotation, Object annotatedElement) {
|
||||
super(annotation.annotationType(), annotatedElement, annotation);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ import java.util.Map;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link AnnotationAttributeExtractor} strategy that
|
||||
* is backed by a {@link Map}.
|
||||
@@ -90,9 +88,9 @@ class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttrib
|
||||
Map<String, Object> originalAttributes, Class<? extends Annotation> annotationType) {
|
||||
|
||||
Map<String, Object> attributes = new LinkedHashMap<>(originalAttributes);
|
||||
Map<String, List<String>> attributeAliasMap = getAttributeAliasMap(annotationType);
|
||||
Map<String, List<String>> attributeAliasMap = AnnotationUtils.getAttributeAliasMap(annotationType);
|
||||
|
||||
for (Method attributeMethod : getAttributeMethods(annotationType)) {
|
||||
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType)) {
|
||||
String attributeName = attributeMethod.getName();
|
||||
Object attributeValue = attributes.get(attributeName);
|
||||
|
||||
@@ -113,7 +111,7 @@ class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttrib
|
||||
|
||||
// if aliases not present, check default
|
||||
if (attributeValue == null) {
|
||||
Object defaultValue = getDefaultValue(annotationType, attributeName);
|
||||
Object defaultValue = AnnotationUtils.getDefaultValue(annotationType, attributeName);
|
||||
if (defaultValue != null) {
|
||||
attributeValue = defaultValue;
|
||||
attributes.put(attributeName, attributeValue);
|
||||
@@ -146,7 +144,7 @@ class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttrib
|
||||
Class<? extends Annotation> nestedAnnotationType =
|
||||
(Class<? extends Annotation>) requiredReturnType;
|
||||
Map<String, Object> map = (Map<String, Object>) attributeValue;
|
||||
attributes.put(attributeName, synthesizeAnnotation(map, nestedAnnotationType, null));
|
||||
attributes.put(attributeName, AnnotationUtils.synthesizeAnnotation(map, nestedAnnotationType, null));
|
||||
converted = true;
|
||||
}
|
||||
|
||||
@@ -157,7 +155,7 @@ class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttrib
|
||||
Class<? extends Annotation> nestedAnnotationType =
|
||||
(Class<? extends Annotation>) requiredReturnType.getComponentType();
|
||||
Map<String, Object>[] maps = (Map<String, Object>[]) attributeValue;
|
||||
attributes.put(attributeName, synthesizeAnnotationArray(maps, nestedAnnotationType));
|
||||
attributes.put(attributeName, AnnotationUtils.synthesizeAnnotationArray(maps, nestedAnnotationType));
|
||||
converted = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
/**
|
||||
* {@link InvocationHandler} for an {@link Annotation} that Spring has
|
||||
* <em>synthesized</em> (i.e., wrapped in a dynamic proxy) with additional
|
||||
@@ -63,22 +61,21 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (isEqualsMethod(method)) {
|
||||
if (ReflectionUtils.isEqualsMethod(method)) {
|
||||
return annotationEquals(args[0]);
|
||||
}
|
||||
if (isHashCodeMethod(method)) {
|
||||
if (ReflectionUtils.isHashCodeMethod(method)) {
|
||||
return annotationHashCode();
|
||||
}
|
||||
if (isToStringMethod(method)) {
|
||||
if (ReflectionUtils.isToStringMethod(method)) {
|
||||
return annotationToString();
|
||||
}
|
||||
if (isAnnotationTypeMethod(method)) {
|
||||
if (AnnotationUtils.isAnnotationTypeMethod(method)) {
|
||||
return annotationType();
|
||||
}
|
||||
if (!isAttributeMethod(method)) {
|
||||
String msg = String.format("Method [%s] is unsupported for synthesized annotation type [%s]",
|
||||
method, annotationType());
|
||||
throw new AnnotationConfigurationException(msg);
|
||||
if (!AnnotationUtils.isAttributeMethod(method)) {
|
||||
throw new AnnotationConfigurationException(String.format(
|
||||
"Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType()));
|
||||
}
|
||||
return getAttributeValue(method);
|
||||
}
|
||||
@@ -100,10 +97,10 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||
|
||||
// Synthesize nested annotations before returning them.
|
||||
if (value instanceof Annotation) {
|
||||
value = synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement());
|
||||
value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement());
|
||||
}
|
||||
else if (value instanceof Annotation[]) {
|
||||
value = synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement());
|
||||
value = AnnotationUtils.synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement());
|
||||
}
|
||||
|
||||
this.valueCache.put(attributeName, value);
|
||||
@@ -164,9 +161,9 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Method attributeMethod : getAttributeMethods(annotationType())) {
|
||||
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType())) {
|
||||
Object thisValue = getAttributeValue(attributeMethod);
|
||||
Object otherValue = invokeMethod(attributeMethod, other);
|
||||
Object otherValue = ReflectionUtils.invokeMethod(attributeMethod, other);
|
||||
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
|
||||
return false;
|
||||
}
|
||||
@@ -181,7 +178,7 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||
private int annotationHashCode() {
|
||||
int result = 0;
|
||||
|
||||
for (Method attributeMethod : getAttributeMethods(annotationType())) {
|
||||
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType())) {
|
||||
Object value = getAttributeValue(attributeMethod);
|
||||
int hashCode;
|
||||
if (value.getClass().isArray()) {
|
||||
@@ -239,7 +236,7 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||
private String annotationToString() {
|
||||
StringBuilder sb = new StringBuilder("@").append(annotationType().getName()).append("(");
|
||||
|
||||
Iterator<Method> iterator = getAttributeMethods(annotationType()).iterator();
|
||||
Iterator<Method> iterator = AnnotationUtils.getAttributeMethods(annotationType()).iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Method attributeMethod = iterator.next();
|
||||
sb.append(attributeMethod.getName());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -58,7 +58,7 @@ abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
|
||||
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
|
||||
AnnotationAttributes nestedAttributes = new AnnotationAttributes();
|
||||
AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader);
|
||||
this.attributes.put(attributeName, nestedAttributes);
|
||||
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
|
||||
|
||||
private final String annotationType;
|
||||
|
||||
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
|
||||
|
||||
private final Map<String, Set<String>> metaAnnotationMap;
|
||||
@@ -55,38 +53,41 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib
|
||||
MultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap,
|
||||
ClassLoader classLoader) {
|
||||
|
||||
super(annotationType, new AnnotationAttributes(), classLoader);
|
||||
this.annotationType = annotationType;
|
||||
super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader);
|
||||
this.attributesMap = attributesMap;
|
||||
this.metaAnnotationMap = metaAnnotationMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doVisitEnd(Class<?> annotationClass) {
|
||||
super.doVisitEnd(annotationClass);
|
||||
List<AnnotationAttributes> attributes = this.attributesMap.get(this.annotationType);
|
||||
if (attributes == null) {
|
||||
this.attributesMap.add(this.annotationType, this.attributes);
|
||||
}
|
||||
else {
|
||||
attributes.add(0, this.attributes);
|
||||
}
|
||||
Set<Annotation> visited = new LinkedHashSet<>();
|
||||
Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
|
||||
if (!ObjectUtils.isEmpty(metaAnnotations)) {
|
||||
for (Annotation metaAnnotation : metaAnnotations) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
|
||||
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
|
||||
Class<?> annotationClass = this.attributes.annotationType();
|
||||
if (annotationClass != null) {
|
||||
List<AnnotationAttributes> attributeList = this.attributesMap.get(this.annotationType);
|
||||
if (attributeList == null) {
|
||||
this.attributesMap.add(this.annotationType, this.attributes);
|
||||
}
|
||||
else {
|
||||
attributeList.add(0, this.attributes);
|
||||
}
|
||||
Set<Annotation> visited = new LinkedHashSet<>();
|
||||
Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
|
||||
if (!ObjectUtils.isEmpty(metaAnnotations)) {
|
||||
for (Annotation metaAnnotation : metaAnnotations) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
|
||||
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.metaAnnotationMap != null) {
|
||||
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
|
||||
for (Annotation ann : visited) {
|
||||
metaAnnotationTypeNames.add(ann.annotationType().getName());
|
||||
if (this.metaAnnotationMap != null) {
|
||||
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
|
||||
for (Annotation ann : visited) {
|
||||
metaAnnotationTypeNames.add(ann.annotationType().getName());
|
||||
}
|
||||
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
|
||||
}
|
||||
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,8 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
|
||||
public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
|
||||
AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes(
|
||||
this.attributesMap, this.metaAnnotationMap, annotationName);
|
||||
return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw, classValuesAsString);
|
||||
return AnnotationReadingVisitorUtils.convertClassValues(
|
||||
"class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -148,7 +149,7 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
|
||||
}
|
||||
for (AnnotationAttributes raw : attributes) {
|
||||
for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
|
||||
this.classLoader, raw, classValuesAsString).entrySet()) {
|
||||
"class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString).entrySet()) {
|
||||
allAttributes.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,25 +41,29 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
abstract class AnnotationReadingVisitorUtils {
|
||||
|
||||
public static AnnotationAttributes convertClassValues(ClassLoader classLoader, AnnotationAttributes original,
|
||||
boolean classValuesAsString) {
|
||||
public static AnnotationAttributes convertClassValues(Object annotatedElement,
|
||||
ClassLoader classLoader, AnnotationAttributes original, boolean classValuesAsString) {
|
||||
|
||||
if (original == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationAttributes result = new AnnotationAttributes(original.size());
|
||||
for (Map.Entry<String, Object> entry : original.entrySet()) {
|
||||
AnnotationAttributes result = new AnnotationAttributes(original);
|
||||
AnnotationUtils.postProcessAnnotationAttributes(annotatedElement, result, classValuesAsString);
|
||||
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
try {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof AnnotationAttributes) {
|
||||
value = convertClassValues(classLoader, (AnnotationAttributes) value, classValuesAsString);
|
||||
value = convertClassValues(
|
||||
annotatedElement, classLoader, (AnnotationAttributes) value, classValuesAsString);
|
||||
}
|
||||
else if (value instanceof AnnotationAttributes[]) {
|
||||
AnnotationAttributes[] values = (AnnotationAttributes[]) value;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = convertClassValues(classLoader, values[i], classValuesAsString);
|
||||
values[i] = convertClassValues(annotatedElement, classLoader, values[i], classValuesAsString);
|
||||
}
|
||||
value = values;
|
||||
}
|
||||
else if (value instanceof Type) {
|
||||
value = (classValuesAsString ? ((Type) value).getClassName() :
|
||||
@@ -67,7 +71,8 @@ abstract class AnnotationReadingVisitorUtils {
|
||||
}
|
||||
else if (value instanceof Type[]) {
|
||||
Type[] array = (Type[]) value;
|
||||
Object[] convArray = (classValuesAsString ? new String[array.length] : new Class<?>[array.length]);
|
||||
Object[] convArray =
|
||||
(classValuesAsString ? new String[array.length] : new Class<?>[array.length]);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
convArray[i] = (classValuesAsString ? array[i].getClassName() :
|
||||
classLoader.loadClass(array[i].getClassName()));
|
||||
@@ -75,11 +80,11 @@ abstract class AnnotationReadingVisitorUtils {
|
||||
value = convArray;
|
||||
}
|
||||
else if (classValuesAsString) {
|
||||
if (value instanceof Class) {
|
||||
if (value instanceof Class<?>) {
|
||||
value = ((Class<?>) value).getName();
|
||||
}
|
||||
else if (value instanceof Class[]) {
|
||||
Class<?>[] clazzArray = (Class[]) value;
|
||||
else if (value instanceof Class<?>[]) {
|
||||
Class<?>[] clazzArray = (Class<?>[]) value;
|
||||
String[] newValue = new String[clazzArray.length];
|
||||
for (int i = 0; i < clazzArray.length; i++) {
|
||||
newValue[i] = clazzArray[i].getName();
|
||||
@@ -87,13 +92,14 @@ abstract class AnnotationReadingVisitorUtils {
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
result.put(entry.getKey(), value);
|
||||
entry.setValue(value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Class not found - can't resolve class reference in annotation attribute.
|
||||
result.put(entry.getKey(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -123,13 +129,12 @@ abstract class AnnotationReadingVisitorUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
// To start with, we populate the results with a copy of all attribute
|
||||
// values from the target annotation. A copy is necessary so that we do
|
||||
// not inadvertently mutate the state of the metadata passed to this
|
||||
// method.
|
||||
AnnotationAttributes results = new AnnotationAttributes(attributesList.get(0));
|
||||
// To start with, we populate the result with a copy of all attribute values
|
||||
// from the target annotation. A copy is necessary so that we do not
|
||||
// inadvertently mutate the state of the metadata passed to this method.
|
||||
AnnotationAttributes result = new AnnotationAttributes(attributesList.get(0));
|
||||
|
||||
Set<String> overridableAttributeNames = new HashSet<>(results.keySet());
|
||||
Set<String> overridableAttributeNames = new HashSet<>(result.keySet());
|
||||
overridableAttributeNames.remove(AnnotationUtils.VALUE);
|
||||
|
||||
// Since the map is a LinkedMultiValueMap, we depend on the ordering of
|
||||
@@ -152,14 +157,14 @@ abstract class AnnotationReadingVisitorUtils {
|
||||
if (value != null) {
|
||||
// Store the value, potentially overriding a value from an attribute
|
||||
// of the same name found higher in the annotation hierarchy.
|
||||
results.put(overridableAttributeName, value);
|
||||
result.put(overridableAttributeName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -122,7 +122,8 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
|
||||
public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
|
||||
AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes(
|
||||
this.attributesMap, this.metaAnnotationMap, annotationName);
|
||||
return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw, classValuesAsString);
|
||||
return AnnotationReadingVisitorUtils.convertClassValues(
|
||||
"method '" + getMethodName() + "'", this.classLoader, raw, classValuesAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,8 +138,9 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
|
||||
}
|
||||
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>();
|
||||
for (AnnotationAttributes annotationAttributes : this.attributesMap.get(annotationName)) {
|
||||
for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
|
||||
this.classLoader, annotationAttributes, classValuesAsString).entrySet()) {
|
||||
AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues(
|
||||
"method '" + getMethodName() + "'", this.classLoader, annotationAttributes, classValuesAsString);
|
||||
for (Map.Entry<String, Object> entry : convertedAttributes.entrySet()) {
|
||||
allAttributes.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
|
||||
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
|
||||
AnnotationAttributes nestedAttributes = new AnnotationAttributes();
|
||||
AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader);
|
||||
this.allNestedAttributes.add(nestedAttributes);
|
||||
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
@@ -30,7 +26,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
*/
|
||||
class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor {
|
||||
|
||||
private final String annotationType;
|
||||
protected final String annotationType;
|
||||
|
||||
|
||||
public RecursiveAnnotationAttributesVisitor(
|
||||
@@ -42,49 +38,8 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
|
||||
|
||||
|
||||
@Override
|
||||
public final void visitEnd() {
|
||||
try {
|
||||
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
|
||||
doVisitEnd(annotationClass);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
logger.debug("Failed to class-load type while reading annotation metadata. " +
|
||||
"This is a non-fatal error, but certain annotation metadata may be unavailable.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doVisitEnd(Class<?> annotationClass) {
|
||||
registerDefaultValues(annotationClass);
|
||||
}
|
||||
|
||||
private void registerDefaultValues(Class<?> annotationClass) {
|
||||
// Only do defaults scanning for public annotations; we'd run into
|
||||
// IllegalAccessExceptions otherwise, and we don't want to mess with
|
||||
// accessibility in a SecurityManager environment.
|
||||
if (Modifier.isPublic(annotationClass.getModifiers())) {
|
||||
// Check declared default values of attributes in the annotation type.
|
||||
Method[] annotationAttributes = annotationClass.getMethods();
|
||||
for (Method annotationAttribute : annotationAttributes) {
|
||||
String attributeName = annotationAttribute.getName();
|
||||
Object defaultValue = annotationAttribute.getDefaultValue();
|
||||
if (defaultValue != null && !this.attributes.containsKey(attributeName)) {
|
||||
if (defaultValue instanceof Annotation) {
|
||||
defaultValue = AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes(
|
||||
(Annotation) defaultValue, false, true));
|
||||
}
|
||||
else if (defaultValue instanceof Annotation[]) {
|
||||
Annotation[] realAnnotations = (Annotation[]) defaultValue;
|
||||
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
||||
for (int i = 0; i < realAnnotations.length; i++) {
|
||||
mappedAnnotations[i] = AnnotationAttributes.fromMap(
|
||||
AnnotationUtils.getAnnotationAttributes(realAnnotations[i], false, true));
|
||||
}
|
||||
defaultValue = mappedAnnotations;
|
||||
}
|
||||
this.attributes.put(attributeName, defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void visitEnd() {
|
||||
AnnotationUtils.registerDefaultValues(this.attributes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user