Skip to content

Commit 0dce570

Browse files
snicolljhoeller
authored andcommitted
Add Qualified element on RootBeanDefinition
Improve RootBeanDefinition to specify an AnnotatedElement that holds qualifier information. When such element is present, any qualifier that it defines will be used to find a matching candidate. Issue: SPR-14725 (cherry picked from commit 2b0bf9f)
1 parent 7ddaf49 commit 0dce570

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 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.
@@ -17,6 +17,7 @@
1717
package org.springframework.beans.factory.annotation;
1818

1919
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
2021
import java.lang.reflect.Method;
2122
import java.util.LinkedHashSet;
2223
import java.util.Map;
@@ -49,6 +50,7 @@
4950
*
5051
* @author Mark Fisher
5152
* @author Juergen Hoeller
53+
* @author Stephane Nicoll
5254
* @since 2.5
5355
* @see AutowireCandidateQualifier
5456
* @see Qualifier
@@ -225,8 +227,12 @@ protected boolean checkQualifier(
225227
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
226228
}
227229
if (qualifier == null) {
228-
// First, check annotation on factory method, if applicable
229-
Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type);
230+
// First, check annotation on qualified element, if any
231+
Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
232+
// Then, check annotation on factory method, if applicable
233+
if (targetAnnotation == null) {
234+
targetAnnotation = getFactoryMethodAnnotation(bd, type);
235+
}
230236
if (targetAnnotation == null) {
231237
RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
232238
if (dbd != null) {
@@ -291,6 +297,11 @@ protected boolean checkQualifier(
291297
return true;
292298
}
293299

300+
protected Annotation getQualifiedElementAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) {
301+
AnnotatedElement qualifiedElement = bd.getQualifiedElement();
302+
return (qualifiedElement != null ? AnnotationUtils.getAnnotation(qualifiedElement, type) : null);
303+
}
304+
294305
protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) {
295306
Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
296307
return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null);

spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.beans.factory.support;
1818

19+
import java.lang.reflect.AnnotatedElement;
1920
import java.lang.reflect.Member;
2021
import java.lang.reflect.Method;
2122
import java.util.HashSet;
@@ -51,12 +52,14 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
5152

5253
private BeanDefinitionHolder decoratedDefinition;
5354

54-
boolean allowCaching = true;
55+
private AnnotatedElement qualifiedElement;
5556

56-
volatile ResolvableType targetType;
57+
boolean allowCaching = true;
5758

5859
boolean isFactoryMethodUnique = false;
5960

61+
volatile ResolvableType targetType;
62+
6063
/** Package-visible field for caching the determined Class of a given bean definition */
6164
volatile Class<?> resolvedTargetType;
6265

@@ -178,9 +181,10 @@ public RootBeanDefinition(String beanClassName, ConstructorArgumentValues cargs,
178181
public RootBeanDefinition(RootBeanDefinition original) {
179182
super(original);
180183
this.decoratedDefinition = original.decoratedDefinition;
184+
this.qualifiedElement = original.qualifiedElement;
181185
this.allowCaching = original.allowCaching;
182-
this.targetType = original.targetType;
183186
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
187+
this.targetType = original.targetType;
184188
}
185189

186190
/**
@@ -219,6 +223,26 @@ public BeanDefinitionHolder getDecoratedDefinition() {
219223
return this.decoratedDefinition;
220224
}
221225

226+
/**
227+
* Specify the {@link AnnotatedElement} defining qualifiers,
228+
* to be used instead of the target class or factory method.
229+
* @since 4.3.3
230+
* @see #setTargetType(ResolvableType)
231+
* @see #getResolvedFactoryMethod()
232+
*/
233+
public void setQualifiedElement(AnnotatedElement qualifiedElement) {
234+
this.qualifiedElement = qualifiedElement;
235+
}
236+
237+
/**
238+
* Return the {@link AnnotatedElement} defining qualifiers, if any.
239+
* Otherwise, the factory method and target class will be checked.
240+
* @since 4.3.3
241+
*/
242+
public AnnotatedElement getQualifiedElement() {
243+
return this.qualifiedElement;
244+
}
245+
222246
/**
223247
* Specify a generics-containing target type of this bean definition, if known in advance.
224248
* @since 4.3.3

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.springframework.tests.sample.beans.IndexedTestBean;
6464
import org.springframework.tests.sample.beans.NestedTestBean;
6565
import org.springframework.tests.sample.beans.TestBean;
66+
import org.springframework.util.ReflectionUtils;
6667
import org.springframework.util.SerializationTestUtils;
6768

6869
import static org.junit.Assert.*;
@@ -1026,14 +1027,35 @@ public void testObjectFactoryQualifierInjection() {
10261027
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
10271028
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
10281029
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
1029-
bf.registerBeanDefinition("testBean", bd);
1030-
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
1030+
bf.registerBeanDefinition("dependencyBean", bd);
1031+
bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class));
10311032

10321033
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
1033-
assertSame(bf.getBean("testBean"), bean.getTestBean());
1034+
assertSame(bf.getBean("dependencyBean"), bean.getTestBean());
1035+
bf.destroySingletons();
1036+
}
1037+
1038+
@Test
1039+
public void testObjectFactoryQualifierProviderInjection() {
1040+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
1041+
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
1042+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
1043+
bpp.setBeanFactory(bf);
1044+
bf.addBeanPostProcessor(bpp);
1045+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
1046+
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
1047+
bd.setQualifiedElement(ReflectionUtils.findMethod(getClass(), "testBeanQualifierProvider"));
1048+
bf.registerBeanDefinition("dependencyBean", bd);
1049+
bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class));
1050+
1051+
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
1052+
assertSame(bf.getBean("dependencyBean"), bean.getTestBean());
10341053
bf.destroySingletons();
10351054
}
10361055

1056+
@Qualifier("testBean")
1057+
private void testBeanQualifierProvider() {}
1058+
10371059
@Test
10381060
public void testObjectFactorySerialization() throws Exception {
10391061
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1588,11 +1610,12 @@ public void testGenericsBasedFieldInjectionWithMocks() {
15881610
rbd.setFactoryBeanName("mocksControl");
15891611
rbd.setFactoryMethodName("createMock");
15901612
rbd.getConstructorArgumentValues().addGenericArgumentValue(Repository.class);
1591-
bf.registerBeanDefinition("integerRepo", rbd);
1613+
rbd.setQualifiedElement(ReflectionUtils.findField(getClass(), "integerRepositoryQualifierProvider"));
1614+
bf.registerBeanDefinition("integerRepository", rbd); // Bean name not matching qualifier
15921615

15931616
RepositoryFieldInjectionBeanWithQualifiers bean = (RepositoryFieldInjectionBeanWithQualifiers) bf.getBean("annotatedBean");
15941617
Repository<?> sr = bf.getBean("stringRepo", Repository.class);
1595-
Repository<?> ir = bf.getBean("integerRepo", Repository.class);
1618+
Repository<?> ir = bf.getBean("integerRepository", Repository.class);
15961619
assertSame(sr, bean.stringRepository);
15971620
assertSame(ir, bean.integerRepository);
15981621
assertSame(1, bean.stringRepositoryArray.length);
@@ -1606,9 +1629,12 @@ public void testGenericsBasedFieldInjectionWithMocks() {
16061629
assertSame(1, bean.stringRepositoryMap.size());
16071630
assertSame(1, bean.integerRepositoryMap.size());
16081631
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
1609-
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
1632+
assertSame(ir, bean.integerRepositoryMap.get("integerRepository"));
16101633
}
16111634

1635+
@Qualifier("integerRepo")
1636+
private Repository<?> integerRepositoryQualifierProvider;
1637+
16121638
@Test
16131639
public void testGenericsBasedFieldInjectionWithSimpleMatch() {
16141640
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();

spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 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.
@@ -126,6 +126,7 @@ public void beanDefinitionMerging() {
126126
bd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5));
127127
bd.getPropertyValues().add("name", "myName");
128128
bd.getPropertyValues().add("age", "99");
129+
bd.setQualifiedElement(getClass());
129130

130131
GenericBeanDefinition childBd = new GenericBeanDefinition();
131132
childBd.setParentName("bd");
@@ -138,6 +139,7 @@ public void beanDefinitionMerging() {
138139

139140
mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(new Integer(9));
140141
assertEquals(new Integer(5), bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue());
142+
assertEquals(getClass(), bd.getQualifiedElement());
141143
}
142144

143145
}

0 commit comments

Comments
 (0)