@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