Introduce 'value' alias for 'attribute' in @AliasFor

SPR-11512 introduced support for annotation attribute aliases via
@AliasFor, requiring the explicit declaration of the 'attribute'
attribute. However, for aliases within an annotation, this explicit
declaration is unnecessary.

This commit improves the readability of alias pairs declared within an
annotation by introducing a 'value' attribute in @AliasFor that is an
alias for the existing 'attribute' attribute. This allows annotations
such as @ContextConfiguration from the spring-test module to declare
aliases as follows.

public @interface ContextConfiguration {

     @AliasFor("locations")
     String[] value() default {};

     @AliasFor("value")
     String[] locations() default {};

    // ...
}

Issue: SPR-13289
This commit is contained in:
Sam Brannen
2015-07-29 15:27:06 +02:00
parent 90493f49e6
commit 725292081e
35 changed files with 170 additions and 84 deletions

View File

@@ -120,9 +120,21 @@ import java.lang.annotation.Target;
public @interface AliasFor {
/**
* The name of the attribute that <em>this</em> attribute is an alias for.
* Alias for {@link #attribute}.
* <p>Intended to be used instead of {@link #attribute} when {@link #annotation}
* is not declared &mdash; for example: {@code @AliasFor("value")} instead of
* {@code @AliasFor(attribute = "value")}.
*/
String attribute();
@AliasFor("attribute")
String value() default "";
/**
* The name of the attribute that <em>this</em> attribute is an alias for.
* @see #value
*/
@AliasFor("value")
String attribute() default "";
/**
* The type of annotation in which the aliased {@link #attribute} is declared.

View File

@@ -1427,7 +1427,7 @@ public abstract class AnnotationUtils {
* @see #getAliasedAttributeName(Method, Class)
*/
static String getAliasedAttributeName(Method attribute) {
return getAliasedAttributeName(attribute, null);
return getAliasedAttributeName(attribute, (Class<? extends Annotation>) null);
}
/**
@@ -1471,7 +1471,7 @@ public abstract class AnnotationUtils {
}
String attributeName = attribute.getName();
String aliasedAttributeName = aliasFor.attribute();
String aliasedAttributeName = getAliasedAttributeName(aliasFor, attribute);
if (!StringUtils.hasText(aliasedAttributeName)) {
String msg = String.format(
@@ -1503,7 +1503,7 @@ public abstract class AnnotationUtils {
throw new AnnotationConfigurationException(msg);
}
String mirrorAliasedAttributeName = mirrorAliasFor.attribute();
String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, aliasedAttribute);
if (!attributeName.equals(mirrorAliasedAttributeName)) {
String msg = String.format(
"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
@@ -1543,6 +1543,38 @@ public abstract class AnnotationUtils {
return aliasedAttributeName;
}
/**
* Get the name of the aliased attribute configured via the supplied
* {@link AliasFor @AliasFor} annotation on the supplied {@code attribute}.
* <p>This method returns the value of either the {@code attribute}
* or {@code value} attribute of {@code @AliasFor}, ensuring that only
* one of the attributes has been declared.
* @param aliasFor the {@code @AliasFor} annotation from which to retrieve
* the aliased attribute name
* @param attribute the attribute that is annotated with {@code @AliasFor},
* used solely for building an exception message
* @return the name of the aliased attribute, potentially an empty string
* @throws AnnotationConfigurationException if invalid configuration of
* {@code @AliasFor} is detected
* @since 4.2
* @see #getAliasedAttributeName(Method, Class)
*/
private static String getAliasedAttributeName(AliasFor aliasFor, Method attribute) {
String attributeName = aliasFor.attribute();
String value = aliasFor.value();
boolean attributeDeclared = StringUtils.hasText(attributeName);
boolean valueDeclared = StringUtils.hasText(value);
if (attributeDeclared && valueDeclared) {
throw new AnnotationConfigurationException(String.format(
"In @AliasFor declared on attribute [%s] in annotation [%s], attribute 'attribute' and its alias 'value' "
+ "are present with values of [%s] and [%s], but only one is permitted.",
attribute.getName(), attribute.getDeclaringClass().getName(), attributeName, value));
}
return (attributeDeclared ? attributeName : value);
}
/**
* Get all methods declared in the supplied {@code annotationType} that
* match Java's requirements for annotation <em>attributes</em>.