diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index d8fcc731e6..0f6d10216c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -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,6 +32,8 @@ import org.springframework.beans.factory.support.AutowireCandidateResolver; import org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -315,25 +317,20 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa * Determine a suggested value from any of the given candidate annotations. */ protected Object findValue(Annotation[] annotationsToSearch) { - for (Annotation annotation : annotationsToSearch) { - if (this.valueAnnotationType.isInstance(annotation)) { - return extractValue(annotation); - } - } - for (Annotation annotation : annotationsToSearch) { - Annotation metaAnn = annotation.annotationType().getAnnotation(this.valueAnnotationType); - if (metaAnn != null) { - return extractValue(metaAnn); - } + AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( + AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); + if (attr != null) { + return extractValue(attr); } return null; } /** * Extract the value attribute from the given annotation. + * @since 4.3 */ - protected Object extractValue(Annotation valueAnnotation) { - Object value = AnnotationUtils.getValue(valueAnnotation); + protected Object extractValue(AnnotationAttributes attr) { + Object value = attr.get(AnnotationUtils.VALUE); if (value == null) { throw new IllegalStateException("Value annotation must have a value attribute"); } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java index 8174725a04..699e7add10 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java @@ -17,6 +17,8 @@ package org.springframework.context.annotation.configuration; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Optional; import javax.inject.Provider; @@ -35,6 +37,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.tests.sample.beans.Colour; @@ -119,6 +122,20 @@ public class AutowiredConfigurationTests { doTestValueInjection(context); } + @Test + public void testValueInjectionWithMetaAnnotation() { + AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(ValueConfigWithMetaAnnotation.class); + doTestValueInjection(context); + } + + @Test + public void testValueInjectionWithAliasedMetaAnnotation() { + AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(ValueConfigWithAliasedMetaAnnotation.class); + doTestValueInjection(context); + } + @Test public void testValueInjectionWithProviderFields() { AnnotationConfigApplicationContext context = @@ -291,6 +308,73 @@ public class AutowiredConfigurationTests { } + @Value("#{systemProperties[myProp]}") + @Retention(RetentionPolicy.RUNTIME) + public @interface MyProp { + } + + + @Configuration + @Scope("prototype") + static class ValueConfigWithMetaAnnotation { + + @MyProp + private String name; + + private String name2; + + @MyProp + public void setName2(String name) { + this.name2 = name; + } + + @Bean @Scope("prototype") + public TestBean testBean() { + return new TestBean(name); + } + + @Bean @Scope("prototype") + public TestBean testBean2() { + return new TestBean(name2); + } + } + + + @Value("") + @Retention(RetentionPolicy.RUNTIME) + public @interface AliasedProp { + + @AliasFor(annotation = Value.class) + String value(); + } + + + @Configuration + @Scope("prototype") + static class ValueConfigWithAliasedMetaAnnotation { + + @AliasedProp("#{systemProperties[myProp]}") + private String name; + + private String name2; + + @AliasedProp("#{systemProperties[myProp]}") + public void setName2(String name) { + this.name2 = name; + } + + @Bean @Scope("prototype") + public TestBean testBean() { + return new TestBean(name); + } + + @Bean @Scope("prototype") + public TestBean testBean2() { + return new TestBean(name2); + } + } + + @Configuration static class ValueConfigWithProviderFields { 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 74549cf2f4..bded44b06a 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 @@ -97,6 +97,35 @@ public class AnnotatedElementUtils { private static final Boolean CONTINUE = null; + /** + * Build an adapted {@link AnnotatedElement} for the given annotations, + * typically for use with other methods on {@link AnnotatedElementUtils}. + * @param annotations the annotations to expose through the {@code AnnotatedElement} + * @since 4.3 + */ + public static AnnotatedElement forAnnotations(final Annotation... annotations) { + return new AnnotatedElement() { + @Override + @SuppressWarnings("unchecked") + public T getAnnotation(Class annotationClass) { + for (Annotation ann : annotations) { + if (ann.annotationType() == annotationClass) { + return (T) ann; + } + } + return null; + } + @Override + public Annotation[] getAnnotations() { + return annotations; + } + @Override + public Annotation[] getDeclaredAnnotations() { + return annotations; + } + }; + } + /** * Get the fully qualified class names of all meta-annotation types * present on the annotation (of the specified {@code annotationType})