diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
index 726e5cf2173..382e3076274 100644
--- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
+++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
@@ -40,9 +40,12 @@ import org.springframework.util.MultiValueMap;
*/
public class AnnotatedElementUtils {
+ private static final Boolean CONTINUE = null;
+
+
/**
- * Get the fully qualified class names of all meta-annotation types
- * present on the annotation (of the specified
+ * Get the fully qualified class names of all meta-annotation
+ * types present on the annotation (of the specified
* {@code annotationType}) on the supplied {@link AnnotatedElement}.
*
*
This method finds all meta-annotations in the annotation hierarchy
@@ -63,8 +66,8 @@ public class AnnotatedElementUtils {
}
/**
- * Get the fully qualified class names of all meta-annotation types
- * present on the annotation (of the specified
+ * Get the fully qualified class names of all meta-annotation
+ * types present on the annotation (of the specified
* {@code annotationType}) on the supplied {@link AnnotatedElement}.
*
*
This method finds all meta-annotations in the annotation hierarchy
@@ -92,7 +95,7 @@ public class AnnotatedElementUtils {
@Override
public Object process(Annotation annotation, int metaDepth) {
types.add(annotation.annotationType().getName());
- return null;
+ return CONTINUE;
}
}, new HashSet(), 1);
}
@@ -126,10 +129,7 @@ public class AnnotatedElementUtils {
@Override
public Boolean process(Annotation annotation, int metaDepth) {
boolean found = annotation.annotationType().getName().equals(annotationType);
- if (found && (metaDepth > 0)) {
- return Boolean.TRUE;
- }
- return null;
+ return ((found && (metaDepth > 0)) ? Boolean.TRUE : CONTINUE);
}
}));
}
@@ -155,7 +155,7 @@ public class AnnotatedElementUtils {
@Override
public Boolean process(Annotation annotation, int metaDepth) {
boolean found = annotation.annotationType().getName().equals(annotationType);
- return (found ? Boolean.TRUE : null);
+ return (found ? Boolean.TRUE : CONTINUE);
}
}));
}
@@ -197,7 +197,7 @@ public class AnnotatedElementUtils {
*/
public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType,
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
- return processWithGetSemantics(element, annotationType, new MergeAnnotationAttributesProcessor(annotationType,
+ return processWithGetSemantics(element, annotationType, new MergedAnnotationAttributesProcessor(annotationType,
classValuesAsString, nestedAnnotationsAsMap));
}
@@ -215,6 +215,7 @@ public class AnnotatedElementUtils {
* @param annotationType the annotation type to find; never {@code null}
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
+ * @since 4.2
*/
public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element,
Class extends Annotation> annotationType) {
@@ -237,6 +238,7 @@ public class AnnotatedElementUtils {
* type to find; never {@code null} or empty
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
+ * @since 4.2
*/
public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, String annotationType) {
return findAnnotationAttributes(element, annotationType, false, false);
@@ -262,6 +264,7 @@ public class AnnotatedElementUtils {
* as Annotation instances
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
+ * @since 4.2
*/
public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, String annotationType,
boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
@@ -292,28 +295,47 @@ public class AnnotatedElementUtils {
* as Annotation instances
* @return the merged {@code AnnotationAttributes}, or {@code null} if
* not found
+ * @since 4.2
*/
private static AnnotationAttributes findAnnotationAttributes(AnnotatedElement element, String annotationType,
boolean searchOnInterfaces, boolean searchOnSuperclasses, boolean searchOnMethodsInInterfaces,
boolean searchOnMethodsInSuperclasses, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
return processWithFindSemantics(element, annotationType, searchOnInterfaces, searchOnSuperclasses,
- searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses, new MergeAnnotationAttributesProcessor(
+ searchOnMethodsInInterfaces, searchOnMethodsInSuperclasses, new MergedAnnotationAttributesProcessor(
annotationType, classValuesAsString, nestedAnnotationsAsMap));
}
/**
+ * Get the annotation attributes of all annotations
+ * of the specified {@code annotationType} in the annotation hierarchy above
+ * the supplied {@link AnnotatedElement} and store the results in a
+ * {@link MultiValueMap}.
+ *
+ * Note: in contrast to {@link #getAnnotationAttributes(AnnotatedElement, String)},
+ * this method does not take attribute overrides into account.
+ *
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
* type to find; never {@code null} or empty
- * @return a {@link MultiValueMap} containing the annotation attributes
- * from all annotations found, or {@code null} if not found
+ * @return a {@link MultiValueMap} keyed by attribute name, containing
+ * the annotation attributes from all annotations found, or {@code null}
+ * if not found
+ * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
*/
public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element, String annotationType) {
return getAllAnnotationAttributes(element, annotationType, false, false);
}
/**
+ * Get the annotation attributes of all annotations
+ * of the specified {@code annotationType} in the annotation hierarchy above
+ * the supplied {@link AnnotatedElement} and store the results in a
+ * {@link MultiValueMap}.
+ *
+ * Note: in contrast to {@link #getAnnotationAttributes(AnnotatedElement, String)},
+ * this method does not take attribute overrides into account.
+ *
* @param element the annotated element; never {@code null}
* @param annotationType the fully qualified class name of the annotation
* type to find; never {@code null} or empty
@@ -322,39 +344,34 @@ public class AnnotatedElementUtils {
* @param nestedAnnotationsAsMap whether to convert nested Annotation
* instances into {@link AnnotationAttributes} maps or to preserve them
* as Annotation instances
- * @return a {@link MultiValueMap} containing the annotation attributes
- * from all annotations found, or {@code null} if not found
+ * @return a {@link MultiValueMap} keyed by attribute name, containing
+ * the annotation attributes from all annotations found, or {@code null}
+ * if not found
*/
public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element,
final String annotationType, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
- final MultiValueMap attributes = new LinkedMultiValueMap();
+ final MultiValueMap attributesMap = new LinkedMultiValueMap();
+
+ processWithGetSemantics(element, annotationType, new SimpleAnnotationProcessor() {
- processWithGetSemantics(element, annotationType, new Processor() {
@Override
public Void process(Annotation annotation, int metaDepth) {
- if (annotation.annotationType().getName().equals(annotationType)) {
- for (Map.Entry entry : AnnotationUtils.getAnnotationAttributes(
- annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
- attributes.add(entry.getKey(), entry.getValue());
+ boolean found = annotation.annotationType().getName().equals(annotationType);
+ if (found) {
+ AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation,
+ classValuesAsString, nestedAnnotationsAsMap);
+ for (Map.Entry entry : annotationAttributes.entrySet()) {
+ attributesMap.add(entry.getKey(), entry.getValue());
}
}
+
+ // Continue searching...
return null;
}
- @Override
- public void postProcess(Annotation annotation, Void result) {
- for (String key : attributes.keySet()) {
- if (!AnnotationUtils.VALUE.equals(key)) {
- Object value = AnnotationUtils.getValue(annotation, key);
- if (value != null) {
- attributes.add(key, value);
- }
- }
- }
- }
});
- return (attributes.isEmpty() ? null : attributes);
+ return (attributesMap.isEmpty() ? null : attributesMap);
}
/**
@@ -725,8 +742,8 @@ public class AnnotatedElementUtils {
}
/**
- * {@link Processor} that only {@linkplain #process processes} annotations
- * and does not {@link #postProcess} results.
+ * {@link Processor} that {@linkplain #process processes} annotations
+ * but does not {@link #postProcess} results.
* @since 4.2
*/
private abstract static class SimpleAnnotationProcessor implements Processor {
@@ -748,14 +765,14 @@ public class AnnotatedElementUtils {
* @see AnnotationUtils#getAnnotationAttributes(Annotation)
* @since 4.2
*/
- private static class MergeAnnotationAttributesProcessor implements Processor {
+ private static class MergedAnnotationAttributesProcessor implements Processor {
private final String annotationType;
private final boolean classValuesAsString;
private final boolean nestedAnnotationsAsMap;
- MergeAnnotationAttributesProcessor(String annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
+ MergedAnnotationAttributesProcessor(String annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
this.annotationType = annotationType;
this.classValuesAsString = classValuesAsString;
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
@@ -764,16 +781,16 @@ public class AnnotatedElementUtils {
@Override
public AnnotationAttributes process(Annotation annotation, int metaDepth) {
boolean found = annotation.annotationType().getName().equals(annotationType);
- return (!found ? null : AnnotationUtils.getAnnotationAttributes(annotation, classValuesAsString, nestedAnnotationsAsMap));
+ return (found ? AnnotationUtils.getAnnotationAttributes(annotation, classValuesAsString, nestedAnnotationsAsMap) : null);
}
@Override
- public void postProcess(Annotation annotation, AnnotationAttributes result) {
- for (String key : result.keySet()) {
+ public void postProcess(Annotation annotation, AnnotationAttributes attributes) {
+ for (String key : attributes.keySet()) {
if (!AnnotationUtils.VALUE.equals(key)) {
Object value = AnnotationUtils.getValue(annotation, key);
if (value != null) {
- result.put(key, AnnotationUtils.adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
+ attributes.put(key, AnnotationUtils.adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
}
}
}
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
index 7596a0ecaad..b46820fef5b 100644
--- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
+++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java
@@ -118,14 +118,26 @@ public class AnnotatedElementUtilsTests {
assertTrue(isAnnotated(ComposedTransactionalComponentClass.class, ComposedTransactionalComponent.class.getName()));
}
+ @Test
+ public void getAllAnnotationAttributesOnNonAnnotatedClass() {
+ assertNull(getAllAnnotationAttributes(NonAnnotatedClass.class, Transactional.class.getName()));
+ }
+
@Test
public void getAllAnnotationAttributesOnClassWithLocalAnnotation() {
- MultiValueMap attributes = getAllAnnotationAttributes(TxConfig.class,
- Transactional.class.getName());
+ MultiValueMap attributes = getAllAnnotationAttributes(TxConfig.class, Transactional.class.getName());
assertNotNull("Annotation attributes map for @Transactional on TxConfig", attributes);
assertEquals("value for TxConfig.", Arrays.asList("TxConfig"), attributes.get("value"));
}
+ @Test
+ public void getAllAnnotationAttributesFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
+ MultiValueMap attributes = getAllAnnotationAttributes(SubSubClassWithInheritedComposedAnnotation.class,
+ Transactional.class.getName());
+ assertNotNull("Annotation attributes map for @Transactional on SubSubClassWithInheritedComposedAnnotation", attributes);
+ assertEquals(Arrays.asList("composed1"), attributes.get("qualifier"));
+ }
+
/**
* If the "value" entry contains both "DerivedTxConfig" AND "TxConfig", then
* the algorithm is accidentally picking up shadowed annotations of the same
@@ -133,12 +145,11 @@ public class AnnotatedElementUtilsTests {
* logic in {@link org.springframework.context.annotation.ProfileCondition}
* to fail.
*
- * @see org.springframework.core.env.EnvironmentIntegrationTests#mostSpecificDerivedClassDrivesEnvironment_withDevEnvAndDerivedDevConfigClass
+ * @see org.springframework.core.env.EnvironmentSystemIntegrationTests#mostSpecificDerivedClassDrivesEnvironment_withDevEnvAndDerivedDevConfigClass
*/
@Test
public void getAllAnnotationAttributesOnClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() {
- MultiValueMap attributes = getAllAnnotationAttributes(DerivedTxConfig.class,
- Transactional.class.getName());
+ MultiValueMap attributes = getAllAnnotationAttributes(DerivedTxConfig.class, Transactional.class.getName());
assertNotNull("Annotation attributes map for @Transactional on DerivedTxConfig", attributes);
assertEquals("value for DerivedTxConfig.", Arrays.asList("DerivedTxConfig"), attributes.get("value"));
}
@@ -146,7 +157,7 @@ public class AnnotatedElementUtilsTests {
/**
* Note: this functionality is required by {@link org.springframework.context.annotation.ProfileCondition}.
*
- * @see org.springframework.core.env.EnvironmentIntegrationTests
+ * @see org.springframework.core.env.EnvironmentSystemIntegrationTests
*/
@Test
public void getAllAnnotationAttributesOnClassWithMultipleComposedAnnotations() {
@@ -211,7 +222,6 @@ public class AnnotatedElementUtilsTests {
assertNotNull("Should find @Transactional on ConcreteClassWithInheritedAnnotation", attributes);
}
- /** @since 4.2 */
@Test
public void getAnnotationAttributesOnInheritedAnnotationInterface() {
String name = Transactional.class.getName();
@@ -219,56 +229,48 @@ public class AnnotatedElementUtilsTests {
assertNotNull("Should get @Transactional on InheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnInheritedAnnotationInterface() {
AnnotationAttributes attributes = findAnnotationAttributes(InheritedAnnotationInterface.class, Transactional.class);
assertNotNull("Should find @Transactional on InheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnSubInheritedAnnotationInterface() {
AnnotationAttributes attributes = findAnnotationAttributes(SubInheritedAnnotationInterface.class, Transactional.class);
assertNotNull("Should find @Transactional on SubInheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnSubSubInheritedAnnotationInterface() {
AnnotationAttributes attributes = findAnnotationAttributes(SubSubInheritedAnnotationInterface.class, Transactional.class);
assertNotNull("Should find @Transactional on SubSubInheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnNonInheritedAnnotationInterface() {
AnnotationAttributes attributes = findAnnotationAttributes(NonInheritedAnnotationInterface.class, Order.class);
assertNotNull("Should find @Order on NonInheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void getAnnotationAttributesOnNonInheritedAnnotationInterface() {
AnnotationAttributes attributes = getAnnotationAttributes(NonInheritedAnnotationInterface.class, Order.class.getName());
assertNotNull("Should get @Order on NonInheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnSubNonInheritedAnnotationInterface() {
AnnotationAttributes attributes = findAnnotationAttributes(SubNonInheritedAnnotationInterface.class, Order.class);
assertNotNull("Should find @Order on SubNonInheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnSubSubNonInheritedAnnotationInterface() {
AnnotationAttributes attributes = findAnnotationAttributes(SubSubNonInheritedAnnotationInterface.class, Order.class);
assertNotNull("Should find @Order on SubSubNonInheritedAnnotationInterface", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesInheritedFromInterfaceMethod() throws NoSuchMethodException {
Method method = ConcreteClassWithInheritedAnnotation.class.getMethod("handleFromInterface");
@@ -276,7 +278,6 @@ public class AnnotatedElementUtilsTests {
assertNotNull("Should find @Order on ConcreteClassWithInheritedAnnotation.handleFromInterface() method", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesInheritedFromAbstractMethod() throws NoSuchMethodException {
Method method = ConcreteClassWithInheritedAnnotation.class.getMethod("handle");
@@ -328,7 +329,6 @@ public class AnnotatedElementUtilsTests {
assertNotNull("Should find @Order on StringGenericParameter.getFor() bridge method", attributes);
}
- /** @since 4.2 */
@Test
public void findAnnotationAttributesOnClassWithMetaAndLocalTxConfig() {
AnnotationAttributes attributes = findAnnotationAttributes(MetaAndLocalTxConfigClass.class, Transactional.class);
@@ -379,7 +379,7 @@ public class AnnotatedElementUtilsTests {
boolean readOnly() default false;
}
- @Transactional
+ @Transactional(qualifier = "composed1")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@@ -387,7 +387,7 @@ public class AnnotatedElementUtilsTests {
@interface Composed1 {
}
- @Transactional(readOnly = true)
+ @Transactional(qualifier = "composed2", readOnly = true)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented