diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java index d7d86e9670..ddbf312a08 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java @@ -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. @@ -79,9 +79,10 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver { ScopeMetadata metadata = new ScopeMetadata(); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition; - AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType); + AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor( + annDef.getMetadata(), this.scopeAnnotationType); if (attributes != null) { - metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource())); + metadata.setScopeName(attributes.getString("value")); ScopedProxyMode proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = this.defaultProxyMode; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 83dc04c959..937aae3fea 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -122,7 +122,7 @@ class ComponentScanAnnotationParser { } Set basePackages = new LinkedHashSet<>(); - String[] basePackagesArray = componentScan.getAliasedStringArray("basePackages", ComponentScan.class, declaringClass); + String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); @@ -148,7 +148,7 @@ class ComponentScanAnnotationParser { List typeFilters = new ArrayList<>(); FilterType filterType = filterAttributes.getEnum("type"); - for (Class filterClass : filterAttributes.getAliasedClassArray("classes", ComponentScan.Filter.class, null)) { + for (Class filterClass : filterAttributes.getClassArray("classes")) { switch (filterType) { case ANNOTATION: Assert.isAssignable(Annotation.class, filterClass, diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 79eeb462d1..ea437e85fa 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -236,7 +236,7 @@ class ConfigurationClassBeanDefinitionReader { ScopedProxyMode proxyMode = ScopedProxyMode.NO; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); if (attributes != null) { - beanDef.setScope(attributes.getAliasedString("value", Scope.class, configClass.getResource())); + beanDef.setScope(attributes.getString("value")); proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index d467d4f500..f912371599 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -287,8 +287,9 @@ class ConfigurationClassParser { // Process any @ImportResource annotations if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { - AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); - String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass); + AnnotationAttributes importResource = + AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); + String[] resources = importResource.getStringArray("locations"); Class readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 23624d135d..8c0c5f2b6a 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -955,7 +955,7 @@ public class ConfigurationClassPostProcessorTests { @ComponentScan(basePackages = "org.springframework.context.annotation.componentscan.simple") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfiguration { + public @interface ComposedConfiguration { } @ComposedConfiguration @@ -966,7 +966,7 @@ public class ConfigurationClassPostProcessorTests { @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfigurationWithAttributeOverrides { + public @interface ComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; @@ -985,7 +985,7 @@ public class ConfigurationClassPostProcessorTests { @ComposedConfigurationWithAttributeOverrides @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedComposedConfigurationWithAttributeOverrides { + public @interface ComposedComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; } @@ -997,14 +997,14 @@ public class ConfigurationClassPostProcessorTests { @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface MetaComponentScan { + public @interface MetaComponentScan { } @MetaComponentScan @Configuration @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface MetaComponentScanConfigurationWithAttributeOverrides { + public @interface MetaComponentScanConfigurationWithAttributeOverrides { String[] basePackages() default {}; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java index 0febd916c2..bc44e15ff5 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java @@ -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 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 implements AnnotationAttributeExtractor { private final Class annotationType; - private final AnnotatedElement annotatedElement; + private final Object annotatedElement; private final S source; @@ -56,7 +56,7 @@ abstract class AbstractAliasAwareAnnotationAttributeExtractor implements Anno * @param source the underlying source of annotation attributes; never {@code null} */ AbstractAliasAwareAnnotationAttributeExtractor( - Class annotationType, AnnotatedElement annotatedElement, S source) { + Class 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 implements Anno } @Override - public final AnnotatedElement getAnnotatedElement() { + public final Object getAnnotatedElement() { return this.annotatedElement; } @@ -89,18 +89,18 @@ abstract class AbstractAliasAwareAnnotationAttributeExtractor implements Anno List 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))); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java index 86ca194bce..7fd8dd4263 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java @@ -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 { * 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. diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java index 61f002357a..d4c74b3a74 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java @@ -50,30 +50,21 @@ import org.springframework.util.StringUtils; @SuppressWarnings("serial") public class AnnotationAttributes extends LinkedHashMap { - private final Class annotationType; + private static final String UNKNOWN = "unknown"; + + private Class 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 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 { 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 key-value pairs. - * @param map original source of annotation attribute key-value - * 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 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) 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 key-value pairs. + * @param map original source of annotation attribute key-value pairs * @see #fromMap(Map) */ public AnnotationAttributes(Map 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 key-value pairs. + * @param other original source of annotation attribute key-value 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 { 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}. - *

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 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 { 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}. - *

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 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 { 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}. - *

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 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 { */ @SuppressWarnings("unchecked") private T getRequiredAttribute(String attributeName, Class 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 { private T getRequiredAttributeWithAlias(String attributeName, Class annotationType, Object annotationSource, Class 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 { 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); diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index da7d6df6d4..53f31bdc32 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -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 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. + *

Specifically, this method enforces attribute alias 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}. *

Specifically, this method enforces attribute alias 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 valuesAlreadyReplaced = new HashSet<>(); - // Validate @AliasFor configuration - Map> 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> 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 synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) { + return synthesizeAnnotation(annotation, (Object) annotatedElement); + } + + @SuppressWarnings("unchecked") + static 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[] synthesizeAnnotationArray(Map[] maps, Class annotationType) { @@ -1551,7 +1625,7 @@ public abstract class AnnotationUtils { private static boolean isSynthesizable(Class 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; } /** diff --git a/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java index 805c769b78..dfcf28ad17 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java @@ -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 { @@ -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); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java index 7789c36c5f..24bdb15dc1 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java @@ -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 originalAttributes, Class annotationType) { Map attributes = new LinkedHashMap<>(originalAttributes); - Map> attributeAliasMap = getAttributeAliasMap(annotationType); + Map> 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 nestedAnnotationType = (Class) requiredReturnType; Map map = (Map) 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 nestedAnnotationType = (Class) requiredReturnType.getComponentType(); Map[] maps = (Map[]) attributeValue; - attributes.put(attributeName, synthesizeAnnotationArray(maps, nestedAnnotationType)); + attributes.put(attributeName, AnnotationUtils.synthesizeAnnotationArray(maps, nestedAnnotationType)); converted = true; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java index 1f07ed2592..74d928b13c 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java @@ -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 * synthesized (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 iterator = getAttributeMethods(annotationType()).iterator(); + Iterator iterator = AnnotationUtils.getAttributeMethods(annotationType()).iterator(); while (iterator.hasNext()) { Method attributeMethod = iterator.next(); sb.append(attributeMethod.getName()); diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java index 2cc12717b2..bd42e059d3 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java @@ -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); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java index 7dc0631385..f67d6bc641 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java @@ -44,8 +44,6 @@ import org.springframework.util.ObjectUtils; */ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor { - private final String annotationType; - private final MultiValueMap attributesMap; private final Map> metaAnnotationMap; @@ -55,38 +53,41 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib MultiValueMap attributesMap, Map> 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 attributes = this.attributesMap.get(this.annotationType); - if (attributes == null) { - this.attributesMap.add(this.annotationType, this.attributes); - } - else { - attributes.add(0, this.attributes); - } - Set 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 attributeList = this.attributesMap.get(this.annotationType); + if (attributeList == null) { + this.attributesMap.add(this.annotationType, this.attributes); + } + else { + attributeList.add(0, this.attributes); + } + Set 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 metaAnnotationTypeNames = new LinkedHashSet<>(visited.size()); - for (Annotation ann : visited) { - metaAnnotationTypeNames.add(ann.annotationType().getName()); + if (this.metaAnnotationMap != null) { + Set 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); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java index ade5e77204..9e83950374 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java @@ -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 entry : AnnotationReadingVisitorUtils.convertClassValues( - this.classLoader, raw, classValuesAsString).entrySet()) { + "class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString).entrySet()) { allAttributes.add(entry.getKey(), entry.getValue()); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java index f6e71a94f2..8ad70d6b69 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java @@ -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 entry : original.entrySet()) { + AnnotationAttributes result = new AnnotationAttributes(original); + AnnotationUtils.postProcessAnnotationAttributes(annotatedElement, result, classValuesAsString); + + for (Map.Entry 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 overridableAttributeNames = new HashSet<>(results.keySet()); + Set 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; } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java index 240797a1c5..4b56f54243 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java @@ -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 allAttributes = new LinkedMultiValueMap<>(); for (AnnotationAttributes annotationAttributes : this.attributesMap.get(annotationName)) { - for (Map.Entry entry : AnnotationReadingVisitorUtils.convertClassValues( - this.classLoader, annotationAttributes, classValuesAsString).entrySet()) { + AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues( + "method '" + getMethodName() + "'", this.classLoader, annotationAttributes, classValuesAsString); + for (Map.Entry entry : convertedAttributes.entrySet()) { allAttributes.add(entry.getKey(), entry.getValue()); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java index f2ec3f55c6..f0c7d20605 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java @@ -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); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java index 0d2176f015..ff7b92fba5 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java @@ -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); } } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java index 57cdd7ab2e..99818400e6 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java @@ -18,16 +18,11 @@ package org.springframework.core.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.springframework.core.annotation.AnnotationUtilsTests.ContextConfig; -import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig; - import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -133,21 +128,21 @@ public class AnnotationAttributesTests { @Test public void getEnumWithNullAttributeName() { exception.expect(IllegalArgumentException.class); - exception.expectMessage(containsString("attributeName must not be null or empty")); + exception.expectMessage("must not be null or empty"); attributes.getEnum(null); } @Test public void getEnumWithEmptyAttributeName() { exception.expect(IllegalArgumentException.class); - exception.expectMessage(containsString("attributeName must not be null or empty")); + exception.expectMessage("must not be null or empty"); attributes.getEnum(""); } @Test public void getEnumWithUnknownAttributeName() { exception.expect(IllegalArgumentException.class); - exception.expectMessage(containsString("Attribute 'bogus' not found")); + exception.expectMessage("Attribute 'bogus' not found"); attributes.getEnum("bogus"); } @@ -159,337 +154,6 @@ public class AnnotationAttributesTests { attributes.getEnum("color"); } - @Test - public void getAliasedString() { - final String value = "metaverse"; - - attributes.clear(); - attributes.put("name", value); - assertEquals(value, getAliasedString("name")); - assertEquals(value, getAliasedString("value")); - - attributes.clear(); - attributes.put("value", value); - assertEquals(value, getAliasedString("name")); - assertEquals(value, getAliasedString("value")); - - attributes.clear(); - attributes.put("name", value); - attributes.put("value", value); - assertEquals(value, getAliasedString("name")); - assertEquals(value, getAliasedString("value")); - } - - @Test - public void getAliasedStringWithImplicitAliases() { - final String value = "metaverse"; - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - attributes.put("value", value); - aliases.stream().forEach(alias -> assertEquals(value, getAliasedStringWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", value); - aliases.stream().forEach(alias -> assertEquals(value, getAliasedStringWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("value", value); - attributes.put("location1", value); - attributes.put("xmlFile", value); - attributes.put("groovyScript", value); - aliases.stream().forEach(alias -> assertEquals(value, getAliasedStringWithImplicitAliases(alias))); - } - - @Test - public void getAliasedStringWithImplicitAliasesWithMissingAliasedAttributes() { - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - exception.expect(IllegalArgumentException.class); - exception.expectMessage(startsWith("Neither attribute 'value' nor one of its aliases [")); - aliases.stream().forEach(alias -> exception.expectMessage(containsString(alias))); - exception.expectMessage(endsWith("] was found in attributes for annotation [" + ImplicitAliasesContextConfig.class.getName() + "]")); - getAliasedStringWithImplicitAliases("value"); - } - - @Test - public void getAliasedStringFromSynthesizedAnnotationAttributes() { - Scope scope = ScopedComponent.class.getAnnotation(Scope.class); - AnnotationAttributes scopeAttributes = AnnotationUtils.getAnnotationAttributes(ScopedComponent.class, scope); - - assertEquals("custom", getAliasedString(scopeAttributes, "name")); - assertEquals("custom", getAliasedString(scopeAttributes, "value")); - } - - @Test - public void getAliasedStringWithMissingAliasedAttributes() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage(equalTo("Neither attribute 'name' nor one of its aliases [value] was found in attributes for annotation [unknown]")); - getAliasedString("name"); - } - - @Test - public void getAliasedStringWithDifferentAliasedValues() { - attributes.put("name", "request"); - attributes.put("value", "session"); - - exception.expect(AnnotationConfigurationException.class); - exception.expectMessage(containsString("In annotation [" + Scope.class.getName() + "]")); - exception.expectMessage(containsString("attribute [name] and its alias [value]")); - exception.expectMessage(containsString("[request] and [session]")); - exception.expectMessage(containsString("but only one is permitted")); - - getAliasedString("name"); - } - - private String getAliasedString(String attributeName) { - return getAliasedString(this.attributes, attributeName); - } - - private String getAliasedString(AnnotationAttributes attrs, String attributeName) { - return attrs.getAliasedString(attributeName, Scope.class, null); - } - - private String getAliasedStringWithImplicitAliases(String attributeName) { - return this.attributes.getAliasedString(attributeName, ImplicitAliasesContextConfig.class, null); - } - - @Test - public void getAliasedStringArray() { - final String[] INPUT = new String[] {"test.xml"}; - final String[] EMPTY = new String[0]; - - attributes.clear(); - attributes.put("location", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", INPUT); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", INPUT); - attributes.put("value", EMPTY); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", EMPTY); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", EMPTY); - attributes.put("value", EMPTY); - assertArrayEquals(EMPTY, getAliasedStringArray("location")); - assertArrayEquals(EMPTY, getAliasedStringArray("value")); - } - - @Test - public void getAliasedStringArrayWithImplicitAliases() { - final String[] INPUT = new String[] {"test.xml"}; - final String[] EMPTY = new String[0]; - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - attributes.put("location1", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(EMPTY, getAliasedStringArrayWithImplicitAliases(alias))); - } - - @Test - public void getAliasedStringArrayWithImplicitAliasesWithMissingAliasedAttributes() { - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - exception.expect(IllegalArgumentException.class); - exception.expectMessage(startsWith("Neither attribute 'value' nor one of its aliases [")); - aliases.stream().forEach(alias -> exception.expectMessage(containsString(alias))); - exception.expectMessage(endsWith("] was found in attributes for annotation [" + ImplicitAliasesContextConfig.class.getName() + "]")); - getAliasedStringArrayWithImplicitAliases("value"); - } - - @Test - public void getAliasedStringArrayWithMissingAliasedAttributes() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage(equalTo("Neither attribute 'location' nor one of its aliases [value] was found in attributes for annotation [unknown]")); - getAliasedStringArray("location"); - } - - @Test - public void getAliasedStringArrayWithDifferentAliasedValues() { - attributes.put("location", new String[] {"1.xml"}); - attributes.put("value", new String[] {"2.xml"}); - - exception.expect(AnnotationConfigurationException.class); - exception.expectMessage(containsString("In annotation [" + ContextConfig.class.getName() + "]")); - exception.expectMessage(containsString("attribute [location] and its alias [value]")); - exception.expectMessage(containsString("[{1.xml}] and [{2.xml}]")); - exception.expectMessage(containsString("but only one is permitted")); - - getAliasedStringArray("location"); - } - - private String[] getAliasedStringArray(String attributeName) { - // Note: even though the attributes we test against here are of type - // String instead of String[], it doesn't matter... since - // AnnotationAttributes does not validate the actual return type of - // attributes in the annotation. - return attributes.getAliasedStringArray(attributeName, ContextConfig.class, null); - } - - private String[] getAliasedStringArrayWithImplicitAliases(String attributeName) { - // Note: even though the attributes we test against here are of type - // String instead of String[], it doesn't matter... since - // AnnotationAttributes does not validate the actual return type of - // attributes in the annotation. - return this.attributes.getAliasedStringArray(attributeName, ImplicitAliasesContextConfig.class, null); - } - - @Test - public void getAliasedClassArray() { - final Class[] INPUT = new Class[] {String.class}; - final Class[] EMPTY = new Class[0]; - - attributes.clear(); - attributes.put("classes", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", INPUT); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", INPUT); - attributes.put("value", EMPTY); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", EMPTY); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", EMPTY); - attributes.put("value", EMPTY); - assertArrayEquals(EMPTY, getAliasedClassArray("classes")); - assertArrayEquals(EMPTY, getAliasedClassArray("value")); - } - - @Test - public void getAliasedClassArrayWithImplicitAliases() { - final Class[] INPUT = new Class[] {String.class}; - final Class[] EMPTY = new Class[0]; - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - attributes.put("location1", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(EMPTY, getAliasedClassArrayWithImplicitAliases(alias))); - } - - @Test - public void getAliasedClassArrayWithMissingAliasedAttributes() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage(equalTo("Neither attribute 'classes' nor one of its aliases [value] was found in attributes for annotation [unknown]")); - getAliasedClassArray("classes"); - } - - @Test - public void getAliasedClassArrayWithDifferentAliasedValues() { - attributes.put("classes", new Class[] {String.class}); - attributes.put("value", new Class[] {Number.class}); - - exception.expect(AnnotationConfigurationException.class); - exception.expectMessage(containsString("In annotation [" + Filter.class.getName() + "]")); - exception.expectMessage(containsString("attribute [classes] and its alias [value]")); - exception.expectMessage(containsString("[{class java.lang.String}] and [{class java.lang.Number}]")); - exception.expectMessage(containsString("but only one is permitted")); - - getAliasedClassArray("classes"); - } - - - private Class[] getAliasedClassArray(String attributeName) { - return attributes.getAliasedClassArray(attributeName, Filter.class, null); - } - - private Class[] getAliasedClassArrayWithImplicitAliases(String attributeName) { - // Note: even though the attributes we test against here are of type - // String instead of Class[], it doesn't matter... since - // AnnotationAttributes does not validate the actual return type of - // attributes in the annotation. - return this.attributes.getAliasedClassArray(attributeName, ImplicitAliasesContextConfig.class, null); - } - enum Color { @@ -514,23 +178,4 @@ public class AnnotationAttributesTests { static class FilteredClass { } - - /** - * Mock of {@code org.springframework.context.annotation.Scope}. - */ - @Retention(RetentionPolicy.RUNTIME) - @interface Scope { - - @AliasFor(attribute = "name") - String value() default "singleton"; - - @AliasFor(attribute = "value") - String name() default "singleton"; - } - - - @Scope(name = "custom") - static class ScopedComponent { - } - } diff --git a/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java index 191192bc47..9a91be3af3 100644 --- a/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java @@ -30,6 +30,7 @@ import java.util.Set; import org.junit.Test; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; @@ -291,6 +292,7 @@ public class AnnotationMetadataTests { Set methods = metadata.getAnnotatedMethods(DirectAnnotation.class.getName()); MethodMetadata method = methods.iterator().next(); assertEquals("direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value")); + assertEquals("direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("myValue")); List allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value"); assertThat(new HashSet<>(allMeta), is(equalTo(new HashSet(Arrays.asList("direct", "meta"))))); allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("additional"); @@ -416,7 +418,11 @@ public class AnnotationMetadataTests { @Retention(RetentionPolicy.RUNTIME) public @interface DirectAnnotation { - String value(); + @AliasFor("myValue") + String value() default ""; + + @AliasFor("value") + String myValue() default ""; String additional() default "direct"; } @@ -449,7 +455,7 @@ public class AnnotationMetadataTests { } // SPR-10914 - public static enum SubclassEnum { + public enum SubclassEnum { FOO { /* Do not delete! This subclassing is intentional. */ }, @@ -489,14 +495,14 @@ public class AnnotationMetadataTests { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Component - public static @interface TestConfiguration { + public @interface TestConfiguration { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface TestComponentScan { + public @interface TestComponentScan { String[] value() default {}; @@ -509,7 +515,7 @@ public class AnnotationMetadataTests { @TestComponentScan(basePackages = "bogus") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfigurationWithAttributeOverrides { + public @interface ComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; } @@ -520,19 +526,19 @@ public class AnnotationMetadataTests { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedAnnotation1 { + public @interface NamedAnnotation1 { String name() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedAnnotation2 { + public @interface NamedAnnotation2 { String name() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedAnnotation3 { + public @interface NamedAnnotation3 { String name() default ""; } @@ -547,7 +553,7 @@ public class AnnotationMetadataTests { @NamedAnnotation3(name = "name 3") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedComposedAnnotation { + public @interface NamedComposedAnnotation { } @NamedComposedAnnotation