Skip to content

Commit 4f95593

Browse files
committed
@value can be used as aliased meta-annotation
Issue: SPR-13603
1 parent 3242ad8 commit 4f95593

File tree

3 files changed

+123
-13
lines changed

3 files changed

+123
-13
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,6 +32,8 @@
3232
import org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver;
3333
import org.springframework.beans.factory.support.RootBeanDefinition;
3434
import org.springframework.core.MethodParameter;
35+
import org.springframework.core.annotation.AnnotatedElementUtils;
36+
import org.springframework.core.annotation.AnnotationAttributes;
3537
import org.springframework.core.annotation.AnnotationUtils;
3638
import org.springframework.util.Assert;
3739
import org.springframework.util.ClassUtils;
@@ -315,25 +317,20 @@ public Object getSuggestedValue(DependencyDescriptor descriptor) {
315317
* Determine a suggested value from any of the given candidate annotations.
316318
*/
317319
protected Object findValue(Annotation[] annotationsToSearch) {
318-
for (Annotation annotation : annotationsToSearch) {
319-
if (this.valueAnnotationType.isInstance(annotation)) {
320-
return extractValue(annotation);
321-
}
322-
}
323-
for (Annotation annotation : annotationsToSearch) {
324-
Annotation metaAnn = annotation.annotationType().getAnnotation(this.valueAnnotationType);
325-
if (metaAnn != null) {
326-
return extractValue(metaAnn);
327-
}
320+
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
321+
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
322+
if (attr != null) {
323+
return extractValue(attr);
328324
}
329325
return null;
330326
}
331327

332328
/**
333329
* Extract the value attribute from the given annotation.
330+
* @since 4.3
334331
*/
335-
protected Object extractValue(Annotation valueAnnotation) {
336-
Object value = AnnotationUtils.getValue(valueAnnotation);
332+
protected Object extractValue(AnnotationAttributes attr) {
333+
Object value = attr.get(AnnotationUtils.VALUE);
337334
if (value == null) {
338335
throw new IllegalStateException("Value annotation must have a value attribute");
339336
}

spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.context.annotation.configuration;
1818

1919
import java.io.IOException;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
2022
import java.util.List;
2123
import java.util.Optional;
2224
import javax.inject.Provider;
@@ -35,6 +37,7 @@
3537
import org.springframework.context.annotation.Scope;
3638
import org.springframework.context.support.ClassPathXmlApplicationContext;
3739
import org.springframework.context.support.GenericApplicationContext;
40+
import org.springframework.core.annotation.AliasFor;
3841
import org.springframework.core.io.ClassPathResource;
3942
import org.springframework.core.io.Resource;
4043
import org.springframework.tests.sample.beans.Colour;
@@ -119,6 +122,20 @@ public void testValueInjection() {
119122
doTestValueInjection(context);
120123
}
121124

125+
@Test
126+
public void testValueInjectionWithMetaAnnotation() {
127+
AnnotationConfigApplicationContext context =
128+
new AnnotationConfigApplicationContext(ValueConfigWithMetaAnnotation.class);
129+
doTestValueInjection(context);
130+
}
131+
132+
@Test
133+
public void testValueInjectionWithAliasedMetaAnnotation() {
134+
AnnotationConfigApplicationContext context =
135+
new AnnotationConfigApplicationContext(ValueConfigWithAliasedMetaAnnotation.class);
136+
doTestValueInjection(context);
137+
}
138+
122139
@Test
123140
public void testValueInjectionWithProviderFields() {
124141
AnnotationConfigApplicationContext context =
@@ -291,6 +308,73 @@ public TestBean testBean2() {
291308
}
292309

293310

311+
@Value("#{systemProperties[myProp]}")
312+
@Retention(RetentionPolicy.RUNTIME)
313+
public @interface MyProp {
314+
}
315+
316+
317+
@Configuration
318+
@Scope("prototype")
319+
static class ValueConfigWithMetaAnnotation {
320+
321+
@MyProp
322+
private String name;
323+
324+
private String name2;
325+
326+
@MyProp
327+
public void setName2(String name) {
328+
this.name2 = name;
329+
}
330+
331+
@Bean @Scope("prototype")
332+
public TestBean testBean() {
333+
return new TestBean(name);
334+
}
335+
336+
@Bean @Scope("prototype")
337+
public TestBean testBean2() {
338+
return new TestBean(name2);
339+
}
340+
}
341+
342+
343+
@Value("")
344+
@Retention(RetentionPolicy.RUNTIME)
345+
public @interface AliasedProp {
346+
347+
@AliasFor(annotation = Value.class)
348+
String value();
349+
}
350+
351+
352+
@Configuration
353+
@Scope("prototype")
354+
static class ValueConfigWithAliasedMetaAnnotation {
355+
356+
@AliasedProp("#{systemProperties[myProp]}")
357+
private String name;
358+
359+
private String name2;
360+
361+
@AliasedProp("#{systemProperties[myProp]}")
362+
public void setName2(String name) {
363+
this.name2 = name;
364+
}
365+
366+
@Bean @Scope("prototype")
367+
public TestBean testBean() {
368+
return new TestBean(name);
369+
}
370+
371+
@Bean @Scope("prototype")
372+
public TestBean testBean2() {
373+
return new TestBean(name2);
374+
}
375+
}
376+
377+
294378
@Configuration
295379
static class ValueConfigWithProviderFields {
296380

spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,35 @@ public class AnnotatedElementUtils {
9797
private static final Boolean CONTINUE = null;
9898

9999

100+
/**
101+
* Build an adapted {@link AnnotatedElement} for the given annotations,
102+
* typically for use with other methods on {@link AnnotatedElementUtils}.
103+
* @param annotations the annotations to expose through the {@code AnnotatedElement}
104+
* @since 4.3
105+
*/
106+
public static AnnotatedElement forAnnotations(final Annotation... annotations) {
107+
return new AnnotatedElement() {
108+
@Override
109+
@SuppressWarnings("unchecked")
110+
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
111+
for (Annotation ann : annotations) {
112+
if (ann.annotationType() == annotationClass) {
113+
return (T) ann;
114+
}
115+
}
116+
return null;
117+
}
118+
@Override
119+
public Annotation[] getAnnotations() {
120+
return annotations;
121+
}
122+
@Override
123+
public Annotation[] getDeclaredAnnotations() {
124+
return annotations;
125+
}
126+
};
127+
}
128+
100129
/**
101130
* Get the fully qualified class names of all meta-annotation types
102131
* <em>present</em> on the annotation (of the specified {@code annotationType})

0 commit comments

Comments
 (0)