提交 72529208 编写于 作者: S Sam Brannen

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
上级 90493f49
......@@ -44,7 +44,7 @@ public @interface CacheEvict {
/**
* Alias for {@link #cacheNames}.
*/
@AliasFor(attribute = "cacheNames")
@AliasFor("cacheNames")
String[] value() default {};
/**
......@@ -55,7 +55,7 @@ public @interface CacheEvict {
* @see #value
* @see CacheConfig#cacheNames
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] cacheNames() default {};
/**
......
......@@ -50,7 +50,7 @@ public @interface CachePut {
/**
* Alias for {@link #cacheNames}.
*/
@AliasFor(attribute = "cacheNames")
@AliasFor("cacheNames")
String[] value() default {};
/**
......@@ -61,7 +61,7 @@ public @interface CachePut {
* @see #value
* @see CacheConfig#cacheNames
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] cacheNames() default {};
/**
......
......@@ -55,7 +55,7 @@ public @interface Cacheable {
/**
* Alias for {@link #cacheNames}.
*/
@AliasFor(attribute = "cacheNames")
@AliasFor("cacheNames")
String[] value() default {};
/**
......@@ -66,7 +66,7 @@ public @interface Cacheable {
* @see #value
* @see CacheConfig#cacheNames
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] cacheNames() default {};
/**
......
......@@ -62,7 +62,7 @@ public @interface ComponentScan {
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
*/
@AliasFor(attribute = "basePackages")
@AliasFor("basePackages")
String[] value() default {};
/**
......@@ -72,7 +72,7 @@ public @interface ComponentScan {
* <p>Use {@link #basePackageClasses} for a type-safe alternative to
* String-based package names.
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] basePackages() default {};
/**
......@@ -166,7 +166,7 @@ public @interface ComponentScan {
* Alias for {@link #classes}.
* @see #classes
*/
@AliasFor(attribute = "classes")
@AliasFor("classes")
Class<?>[] value() default {};
/**
......@@ -190,7 +190,7 @@ public @interface ComponentScan {
* @see #value
* @see #type
*/
@AliasFor(attribute = "value")
@AliasFor("value")
Class<?>[] classes() default {};
/**
......
......@@ -59,7 +59,7 @@ public @interface ImportResource {
* @see #locations
* @see #reader
*/
@AliasFor(attribute = "locations")
@AliasFor("locations")
String[] value() default {};
/**
......@@ -72,7 +72,7 @@ public @interface ImportResource {
* @see #value
* @see #reader
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] locations() default {};
/**
......
......@@ -61,7 +61,7 @@ public @interface Scope {
* Alias for {@link #scopeName}.
* @see #scopeName
*/
@AliasFor(attribute = "scopeName")
@AliasFor("scopeName")
String value() default "";
/**
......@@ -75,7 +75,7 @@ public @interface Scope {
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* @see #value
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String scopeName() default "";
/**
......
......@@ -69,7 +69,7 @@ public @interface EventListener {
/**
* Alias for {@link #classes}.
*/
@AliasFor(attribute = "classes")
@AliasFor("classes")
Class<?>[] value() default {};
/**
......@@ -79,7 +79,7 @@ public @interface EventListener {
* attribute is specified with multiple values, the annotated method
* must <em>not</em> declare any parameters.
*/
@AliasFor(attribute = "value")
@AliasFor("value")
Class<?>[] classes() default {};
/**
......
......@@ -49,10 +49,10 @@ public @interface ManagedResource {
/**
* Alias for the {@link #objectName} attribute, for simple default usage.
*/
@AliasFor(attribute = "objectName")
@AliasFor("objectName")
String value() default "";
@AliasFor(attribute = "value")
@AliasFor("value")
String objectName() default "";
String description() default "";
......
......@@ -119,10 +119,22 @@ import java.lang.annotation.Target;
@Documented
public @interface AliasFor {
/**
* 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")}.
*/
@AliasFor("attribute")
String value() default "";
/**
* The name of the attribute that <em>this</em> attribute is an alias for.
* @see #value
*/
String attribute();
@AliasFor("value")
String attribute() default "";
/**
* The type of annotation in which the aliased {@link #attribute} is declared.
......
......@@ -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>.
......
......@@ -714,10 +714,30 @@ public class AnnotationUtilsTests {
assertEquals("actual value attribute: ", "/test", synthesizedAgainWebMapping.value());
}
@Test
public void synthesizeAnnotationWhereAliasForIsMissingAttributeDeclaration() throws Exception {
AliasForWithMissingAttributeDeclaration annotation = AliasForWithMissingAttributeDeclarationClass.class.getAnnotation(AliasForWithMissingAttributeDeclaration.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(containsString("@AliasFor declaration on attribute [foo] in annotation"));
exception.expectMessage(containsString(AliasForWithMissingAttributeDeclaration.class.getName()));
exception.expectMessage(containsString("is missing required 'attribute' value"));
synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWhereAliasForHasDuplicateAttributeDeclaration() throws Exception {
AliasForWithDuplicateAttributeDeclaration annotation = AliasForWithDuplicateAttributeDeclarationClass.class.getAnnotation(AliasForWithDuplicateAttributeDeclaration.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(containsString("In @AliasFor declared on attribute [foo] in annotation"));
exception.expectMessage(containsString(AliasForWithDuplicateAttributeDeclaration.class.getName()));
exception.expectMessage(containsString("attribute 'attribute' and its alias 'value' are present with values of [baz] and [bar]"));
exception.expectMessage(containsString("but only one is permitted"));
synthesizeAnnotation(annotation);
}
@Test
public void synthesizeAnnotationWithAttributeAliasForNonexistentAttribute() throws Exception {
AliasForNonexistentAttribute annotation =
AliasForNonexistentAttributeClass.class.getAnnotation(AliasForNonexistentAttribute.class);
AliasForNonexistentAttribute annotation = AliasForNonexistentAttributeClass.class.getAnnotation(AliasForNonexistentAttribute.class);
exception.expect(AnnotationConfigurationException.class);
exception.expectMessage(containsString("Attribute [foo] in"));
exception.expectMessage(containsString(AliasForNonexistentAttribute.class.getName()));
......@@ -1434,7 +1454,7 @@ public class AnnotationUtilsTests {
String name();
@AliasFor(attribute = "path")
@AliasFor("path")
String value() default "";
@AliasFor(attribute = "value")
......@@ -1472,10 +1492,10 @@ public class AnnotationUtilsTests {
@Retention(RetentionPolicy.RUNTIME)
@interface ContextConfig {
@AliasFor(attribute = "locations")
@AliasFor("locations")
String value() default "";
@AliasFor(attribute = "value")
@AliasFor("value")
String locations() default "";
}
......@@ -1483,10 +1503,10 @@ public class AnnotationUtilsTests {
@interface BrokenContextConfig {
// Intentionally missing:
// @AliasFor(attribute = "locations")
// @AliasFor("locations")
String value() default "";
@AliasFor(attribute = "value")
@AliasFor("value")
String locations() default "";
}
......@@ -1530,10 +1550,32 @@ public class AnnotationUtilsTests {
}
@Retention(RetentionPolicy.RUNTIME)
@interface AliasForWithMissingAttributeDeclaration {
@AliasFor
String foo() default "";
}
@AliasForWithMissingAttributeDeclaration
static class AliasForWithMissingAttributeDeclarationClass {
}
@Retention(RetentionPolicy.RUNTIME)
@interface AliasForWithDuplicateAttributeDeclaration {
@AliasFor(value = "bar", attribute = "baz")
String foo() default "";
}
@AliasForWithDuplicateAttributeDeclaration
static class AliasForWithDuplicateAttributeDeclarationClass {
}
@Retention(RetentionPolicy.RUNTIME)
@interface AliasForNonexistentAttribute {
@AliasFor(attribute = "bar")
@AliasFor("bar")
String foo() default "";
}
......@@ -1544,7 +1586,7 @@ public class AnnotationUtilsTests {
@Retention(RetentionPolicy.RUNTIME)
@interface AliasForWithoutMirroredAliasFor {
@AliasFor(attribute = "bar")
@AliasFor("bar")
String foo() default "";
String bar() default "";
......@@ -1571,10 +1613,10 @@ public class AnnotationUtilsTests {
@Retention(RetentionPolicy.RUNTIME)
@interface AliasForAttributeOfDifferentType {
@AliasFor(attribute = "bar")
@AliasFor("bar")
String[] foo() default "";
@AliasFor(attribute = "foo")
@AliasFor("foo")
boolean bar() default true;
}
......@@ -1599,10 +1641,10 @@ public class AnnotationUtilsTests {
@Retention(RetentionPolicy.RUNTIME)
@interface AliasForAttributeWithDifferentDefaultValue {
@AliasFor(attribute = "bar")
@AliasFor("bar")
String foo() default "X";
@AliasFor(attribute = "foo")
@AliasFor("foo")
String bar() default "Z";
}
......
......@@ -32,9 +32,9 @@ import org.springframework.core.annotation.AliasFor;
String name();
@AliasFor(attribute = "path")
@AliasFor("path")
String value() default "";
@AliasFor(attribute = "value")
@AliasFor("value")
String path() default "";
}
......@@ -39,14 +39,14 @@ public @interface Header {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
* The name of the request header to bind to.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -43,7 +43,7 @@ public @interface Payload {
/**
* Alias for {@link #expression}.
*/
@AliasFor(attribute = "expression")
@AliasFor("expression")
String value() default "";
/**
......@@ -54,7 +54,7 @@ public @interface Payload {
* <p>When processing STOMP over WebSocket messages this attribute is not supported.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String expression() default "";
/**
......
......@@ -46,7 +46,7 @@ public @interface SendToUser {
* Alias for {@link #destinations}.
* @see #destinations
*/
@AliasFor(attribute = "destinations")
@AliasFor("destinations")
String[] value() default {};
/**
......@@ -57,7 +57,7 @@ public @interface SendToUser {
* @see #value
* @see org.springframework.messaging.simp.annotation.support.SendToMethodReturnValueHandler
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] destinations() default {};
/**
......
......@@ -55,7 +55,7 @@ public @interface ActiveProfiles {
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #profiles}, but it may be used <em>instead</em> of {@link #profiles}.
*/
@AliasFor(attribute = "profiles")
@AliasFor("profiles")
String[] value() default {};
/**
......@@ -64,7 +64,7 @@ public @interface ActiveProfiles {
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #value}, but it may be used <em>instead</em> of {@link #value}.
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] profiles() default {};
/**
......
......@@ -98,7 +98,7 @@ public @interface ContextConfiguration {
* @since 3.0
* @see #inheritLocations
*/
@AliasFor(attribute = "locations")
@AliasFor("locations")
String[] value() default {};
/**
......@@ -129,7 +129,7 @@ public @interface ContextConfiguration {
* @since 2.5
* @see #inheritLocations
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] locations() default {};
/**
......
......@@ -87,7 +87,7 @@ public @interface TestExecutionListeners {
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #listeners}, but it may be used instead of {@link #listeners}.
*/
@AliasFor(attribute = "listeners")
@AliasFor("listeners")
Class<? extends TestExecutionListener>[] value() default {};
/**
......@@ -103,7 +103,7 @@ public @interface TestExecutionListeners {
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
* @see org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
*/
@AliasFor(attribute = "value")
@AliasFor("value")
Class<? extends TestExecutionListener>[] listeners() default {};
/**
......
......@@ -96,7 +96,7 @@ public @interface TestPropertySource {
*
* @see #locations
*/
@AliasFor(attribute = "locations")
@AliasFor("locations")
String[] value() default {};
/**
......@@ -144,7 +144,7 @@ public @interface TestPropertySource {
* @see #properties
* @see org.springframework.core.env.PropertySource
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] locations() default {};
/**
......
......@@ -98,7 +98,7 @@ public @interface Sql {
* @see #scripts
* @see #statements
*/
@AliasFor(attribute = "scripts")
@AliasFor("scripts")
String[] value() default {};
/**
......@@ -139,7 +139,7 @@ public @interface Sql {
* @see #value
* @see #statements
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] scripts() default {};
/**
......
......@@ -60,7 +60,7 @@ public @interface Transactional {
* Alias for {@link #transactionManager}.
* @see #transactionManager
*/
@AliasFor(attribute = "transactionManager")
@AliasFor("transactionManager")
String value() default "";
/**
......@@ -72,7 +72,7 @@ public @interface Transactional {
* @since 4.2
* @see #value
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String transactionManager() default "";
/**
......
......@@ -59,7 +59,7 @@ public @interface TransactionalEventListener {
/**
* Alias for {@link #classes}.
*/
@AliasFor(attribute = "classes")
@AliasFor("classes")
Class<?>[] value() default {};
/**
......@@ -68,7 +68,7 @@ public @interface TransactionalEventListener {
* may or may not be specified. When this attribute is specified with more
* than one value, the method must not have a parameter.
*/
@AliasFor(attribute = "value")
@AliasFor("value")
Class<?>[] classes() default {};
/**
......
......@@ -68,7 +68,7 @@ public @interface ControllerAdvice {
* @since 4.0
* @see #basePackages()
*/
@AliasFor(attribute = "basePackages")
@AliasFor("basePackages")
String[] value() default {};
/**
......@@ -82,7 +82,7 @@ public @interface ControllerAdvice {
* alternative to String-based package names.
* @since 4.0
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] basePackages() default {};
/**
......
......@@ -51,14 +51,14 @@ public @interface CookieValue {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
* The name of the cookie to bind to.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -51,7 +51,7 @@ public @interface CrossOrigin {
/**
* Alias for {@link #origins}.
*/
@AliasFor(attribute = "origins")
@AliasFor("origins")
String[] value() default {};
/**
......@@ -62,7 +62,7 @@ public @interface CrossOrigin {
* <p>If undefined, all origins are allowed.
* @see #value
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] origins() default {};
/**
......
......@@ -50,7 +50,7 @@ public @interface MatrixVariable {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
......@@ -58,7 +58,7 @@ public @interface MatrixVariable {
* @since 4.2
* @see #value
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -51,14 +51,14 @@ public @interface RequestHeader {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
* The name of the request header to bind to.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -309,7 +309,7 @@ public @interface RequestMapping {
* When used at the type level, all method-level mappings inherit
* this primary mapping, narrowing it for a specific handler method.
*/
@AliasFor(attribute = "path")
@AliasFor("path")
String[] value() default {};
/**
......@@ -324,7 +324,7 @@ public @interface RequestMapping {
* @see org.springframework.web.bind.annotation.ValueConstants#DEFAULT_NONE
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] path() default {};
/**
......
......@@ -59,14 +59,14 @@ public @interface RequestParam {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
* The name of the request parameter to bind to.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -67,14 +67,14 @@ public @interface RequestPart {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
* The name of the part in the {@code "multipart/form-data"} request to bind to.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -45,7 +45,7 @@ public @interface ResponseStatus {
/**
* Alias for {@link #code}.
*/
@AliasFor(attribute = "code")
@AliasFor("code")
HttpStatus value() default HttpStatus.INTERNAL_SERVER_ERROR;
/**
......@@ -55,7 +55,7 @@ public @interface ResponseStatus {
* @since 4.2
* @see javax.servlet.http.HttpServletResponse#setStatus(int)
*/
@AliasFor(attribute = "value")
@AliasFor("value")
HttpStatus code() default HttpStatus.INTERNAL_SERVER_ERROR;
/**
......
......@@ -64,7 +64,7 @@ public @interface SessionAttributes {
/**
* Alias for {@link #names}.
*/
@AliasFor(attribute = "names")
@AliasFor("names")
String[] value() default {};
/**
......@@ -76,7 +76,7 @@ public @interface SessionAttributes {
* names but rather operate on the model only.
* @since 4.2
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String[] names() default {};
/**
......
......@@ -42,7 +42,7 @@ public @interface ActionMapping {
/**
* Alias for {@link #name}.
*/
@AliasFor(attribute = "name")
@AliasFor("name")
String value() default "";
/**
......@@ -57,7 +57,7 @@ public @interface ActionMapping {
* @see javax.portlet.ActionRequest#ACTION_NAME
* @see #value
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String name() default "";
/**
......
......@@ -42,7 +42,7 @@ public @interface RenderMapping {
/**
* Alias for {@link #windowState}.
*/
@AliasFor(attribute = "windowState")
@AliasFor("windowState")
String value() default "";
/**
......@@ -56,7 +56,7 @@ public @interface RenderMapping {
* @see #value
* @see javax.portlet.PortletRequest#getWindowState()
*/
@AliasFor(attribute = "value")
@AliasFor("value")
String windowState() default "";
/**
......
......@@ -429,10 +429,10 @@ method has been added.
----
public @interface ContextConfiguration {
@AliasFor(attribute = "locations")
@AliasFor("locations")
String[] value() default {};
@AliasFor(attribute = "value")
@AliasFor("value")
String[] locations() default {};
// ...
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册