@Value can be used as aliased meta-annotation

Issue: SPR-13603
This commit is contained in:
Juergen Hoeller 2015-12-29 18:02:16 +01:00
parent 3242ad8fc4
commit 4f955932a7
3 changed files with 123 additions and 13 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,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");
}

View File

@ -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 {

View File

@ -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 extends Annotation> T getAnnotation(Class<T> 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
* <em>present</em> on the annotation (of the specified {@code annotationType})