Support annotation attribute aliases and overrides via @AliasFor

This commit introduces first-class support for aliases for annotation
attributes. Specifically, this commit introduces a new @AliasFor
annotation that can be used to declare a pair of aliased attributes
within a single annotation or an alias from an attribute in a custom
composed annotation to an attribute in a meta-annotation.

To support @AliasFor within annotation instances, AnnotationUtils has
been overhauled to "synthesize" any annotations returned by "get" and
"find" searches. A SynthesizedAnnotation is an annotation that is
wrapped in a JDK dynamic proxy which provides run-time support for
@AliasFor semantics. SynthesizedAnnotationInvocationHandler is the
actual handler behind the proxy.

In addition, the contract for @AliasFor is fully validated, and an
AnnotationConfigurationException is thrown in case invalid
configuration is detected.

For example, @ContextConfiguration from the spring-test module is now
declared as follows:

    public @interface ContextConfiguration {

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

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

        // ...
    }

The following annotations and their related support classes have been
modified to use @AliasFor.

- @ManagedResource
- @ContextConfiguration
- @ActiveProfiles
- @TestExecutionListeners
- @TestPropertySource
- @Sql
- @ControllerAdvice
- @RequestMapping

Similarly, support for AnnotationAttributes has been reworked to
support @AliasFor as well. This allows for fine-grained control over
exactly which attributes are overridden within an annotation hierarchy.
In fact, it is now possible to declare an alias for the 'value'
attribute of a meta-annotation.

For example, given the revised declaration of @ContextConfiguration
above, one can now develop a composed annotation with a custom
attribute override as follows.

    @ContextConfiguration
    public @interface MyTestConfig {

        @AliasFor(
           annotation = ContextConfiguration.class,
           attribute = "locations"
        )
        String[] xmlFiles();

        // ...
    }

Consequently, the following are functionally equivalent.

- @MyTestConfig(xmlFiles = "test.xml")
- @ContextConfiguration("test.xml")
- @ContextConfiguration(locations = "test.xml").

Issue: SPR-11512, SPR-11513
This commit is contained in:
Sam Brannen
2015-05-14 23:32:30 +02:00
parent a87d5f8a63
commit ca66e076d1
31 changed files with 1582 additions and 336 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -32,7 +32,6 @@ import org.springframework.jmx.export.metadata.ManagedNotification;
import org.springframework.jmx.export.metadata.ManagedOperation;
import org.springframework.jmx.export.metadata.ManagedOperationParameter;
import org.springframework.jmx.export.metadata.ManagedResource;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/**
@@ -66,7 +65,6 @@ public class AnnotationJmxAttributeSource implements JmxAttributeSource, BeanFac
}
}
@Override
public ManagedResource getManagedResource(Class<?> beanClass) throws InvalidMetadataException {
org.springframework.jmx.export.annotation.ManagedResource ann =
@@ -76,13 +74,6 @@ public class AnnotationJmxAttributeSource implements JmxAttributeSource, BeanFac
}
ManagedResource managedResource = new ManagedResource();
AnnotationBeanUtils.copyPropertiesToBean(ann, managedResource, this.embeddedValueResolver);
if (!"".equals(ann.value()) && !StringUtils.hasLength(managedResource.getObjectName())) {
String value = ann.value();
if (this.embeddedValueResolver != null) {
value = this.embeddedValueResolver.resolveStringValue(value);
}
managedResource.setObjectName(value);
}
return managedResource;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2015 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.
@@ -23,6 +23,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* JDK 1.5+ class-level annotation that indicates to register instances of a
* class with a JMX server, corresponding to the ManagedResource attribute.
@@ -34,6 +36,7 @@ import java.lang.annotation.Target;
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.2
* @see org.springframework.jmx.export.metadata.ManagedResource
*/
@@ -44,11 +47,12 @@ import java.lang.annotation.Target;
public @interface ManagedResource {
/**
* The annotation value is equivalent to the {@code objectName}
* attribute, for simple default usage.
* Alias for the {@link #objectName} attribute, for simple default usage.
*/
@AliasFor(attribute = "objectName")
String value() default "";
@AliasFor(attribute = "value")
String objectName() default "";
String description() default "";