Consistent alias processing behind AnnotatedTypeMetadata abstraction (also for ASM)
Issue: SPR-14427
This commit is contained in:
		
							parent
							
								
									b1663585dc
								
							
						
					
					
						commit
						3d3407c789
					
				| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,7 +122,7 @@ class ComponentScanAnnotationParser {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		Set<String> 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<TypeFilter> 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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
 | 
			
		||||
			for (String resource : resources) {
 | 
			
		||||
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {};
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<String> 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<String> 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<String> 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<String> 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<String> 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 {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<MethodMetadata> 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<Object> allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
 | 
			
		||||
		assertThat(new HashSet<>(allMeta), is(equalTo(new HashSet<Object>(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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue