@Value can be used as aliased meta-annotation
Issue: SPR-13603
This commit is contained in:
parent
3242ad8fc4
commit
4f955932a7
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.GenericTypeAwareAutowireCandidateResolver;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.MethodParameter;
|
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.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
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.
|
* Determine a suggested value from any of the given candidate annotations.
|
||||||
*/
|
*/
|
||||||
protected Object findValue(Annotation[] annotationsToSearch) {
|
protected Object findValue(Annotation[] annotationsToSearch) {
|
||||||
for (Annotation annotation : annotationsToSearch) {
|
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
|
||||||
if (this.valueAnnotationType.isInstance(annotation)) {
|
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
|
||||||
return extractValue(annotation);
|
if (attr != null) {
|
||||||
}
|
return extractValue(attr);
|
||||||
}
|
|
||||||
for (Annotation annotation : annotationsToSearch) {
|
|
||||||
Annotation metaAnn = annotation.annotationType().getAnnotation(this.valueAnnotationType);
|
|
||||||
if (metaAnn != null) {
|
|
||||||
return extractValue(metaAnn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the value attribute from the given annotation.
|
* Extract the value attribute from the given annotation.
|
||||||
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
protected Object extractValue(Annotation valueAnnotation) {
|
protected Object extractValue(AnnotationAttributes attr) {
|
||||||
Object value = AnnotationUtils.getValue(valueAnnotation);
|
Object value = attr.get(AnnotationUtils.VALUE);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new IllegalStateException("Value annotation must have a value attribute");
|
throw new IllegalStateException("Value annotation must have a value attribute");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package org.springframework.context.annotation.configuration;
|
package org.springframework.context.annotation.configuration;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
@ -35,6 +37,7 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
import org.springframework.context.support.GenericApplicationContext;
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.tests.sample.beans.Colour;
|
import org.springframework.tests.sample.beans.Colour;
|
||||||
|
|
@ -119,6 +122,20 @@ public class AutowiredConfigurationTests {
|
||||||
doTestValueInjection(context);
|
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
|
@Test
|
||||||
public void testValueInjectionWithProviderFields() {
|
public void testValueInjectionWithProviderFields() {
|
||||||
AnnotationConfigApplicationContext context =
|
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
|
@Configuration
|
||||||
static class ValueConfigWithProviderFields {
|
static class ValueConfigWithProviderFields {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,35 @@ public class AnnotatedElementUtils {
|
||||||
private static final Boolean CONTINUE = null;
|
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
|
* Get the fully qualified class names of all meta-annotation types
|
||||||
* <em>present</em> on the annotation (of the specified {@code annotationType})
|
* <em>present</em> on the annotation (of the specified {@code annotationType})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue