Skip to content

Commit a7849b2

Browse files
committed
DefaultListableBeanFactory does not trigger early candidate creation ahead of primary bean selection
Issue: SPR-14611 (cherry picked from commit c4fcdb6)
1 parent 74bf659 commit a7849b2

File tree

4 files changed

+124
-91
lines changed

4 files changed

+124
-91
lines changed

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

Lines changed: 113 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -986,38 +986,47 @@ public <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws Bea
986986
return null;
987987
}
988988

989+
@SuppressWarnings("unchecked")
989990
private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, Object... args) throws BeansException {
990991
Assert.notNull(requiredType, "Required type must not be null");
991-
String[] beanNames = getBeanNamesForType(requiredType);
992+
String[] candidateNames = getBeanNamesForType(requiredType);
992993

993-
if (beanNames.length > 1) {
994-
ArrayList<String> autowireCandidates = new ArrayList<String>();
995-
for (String beanName : beanNames) {
994+
if (candidateNames.length > 1) {
995+
List<String> autowireCandidates = new ArrayList<String>(candidateNames.length);
996+
for (String beanName : candidateNames) {
996997
if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
997998
autowireCandidates.add(beanName);
998999
}
9991000
}
10001001
if (!autowireCandidates.isEmpty()) {
1001-
beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
1002+
candidateNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
10021003
}
10031004
}
10041005

1005-
if (beanNames.length == 1) {
1006-
String beanName = beanNames[0];
1006+
if (candidateNames.length == 1) {
1007+
String beanName = candidateNames[0];
10071008
return new NamedBeanHolder<T>(beanName, getBean(beanName, requiredType, args));
10081009
}
1009-
else if (beanNames.length > 1) {
1010-
Map<String, Object> candidates = new LinkedHashMap<String, Object>();
1011-
for (String beanName : beanNames) {
1012-
candidates.put(beanName, getBean(beanName, requiredType, args));
1010+
else if (candidateNames.length > 1) {
1011+
Map<String, Object> candidates = new LinkedHashMap<String, Object>(candidateNames.length);
1012+
for (String candidateName : candidateNames) {
1013+
if (containsSingleton(candidateName)) {
1014+
candidates.put(candidateName, getBean(candidateName, requiredType, args));
1015+
}
1016+
else {
1017+
candidates.put(candidateName, getType(candidateName));
1018+
}
10131019
}
1014-
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
1015-
if (primaryCandidate != null) {
1016-
return new NamedBeanHolder<T>(primaryCandidate, getBean(primaryCandidate, requiredType, args));
1020+
String candidateName = determinePrimaryCandidate(candidates, requiredType);
1021+
if (candidateName == null) {
1022+
candidateName = determineHighestPriorityCandidate(candidates, requiredType);
10171023
}
1018-
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
1019-
if (priorityCandidate != null) {
1020-
return new NamedBeanHolder<T>(priorityCandidate, getBean(priorityCandidate, requiredType, args));
1024+
if (candidateName != null) {
1025+
Object beanInstance = candidates.get(candidateName);
1026+
if (beanInstance instanceof Class) {
1027+
beanInstance = getBean(candidateName, requiredType, args);
1028+
}
1029+
return new NamedBeanHolder<T>(candidateName, (T) beanInstance);
10211030
}
10221031
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
10231032
}
@@ -1086,9 +1095,13 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
10861095
}
10871096
return null;
10881097
}
1098+
1099+
String autowiredBeanName;
1100+
Object instanceCandidate;
1101+
10891102
if (matchingBeans.size() > 1) {
1090-
String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
1091-
if (primaryBeanName == null) {
1103+
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
1104+
if (autowiredBeanName == null) {
10921105
if (descriptor.isRequired() || !indicatesMultipleBeans(type)) {
10931106
return descriptor.resolveNotUnique(type, matchingBeans);
10941107
}
@@ -1099,17 +1112,20 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
10991112
return null;
11001113
}
11011114
}
1102-
if (autowiredBeanNames != null) {
1103-
autowiredBeanNames.add(primaryBeanName);
1104-
}
1105-
return matchingBeans.get(primaryBeanName);
1115+
instanceCandidate = matchingBeans.get(autowiredBeanName);
11061116
}
1107-
// We have exactly one match.
1108-
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
1117+
else {
1118+
// We have exactly one match.
1119+
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
1120+
autowiredBeanName = entry.getKey();
1121+
instanceCandidate = entry.getValue();
1122+
}
1123+
11091124
if (autowiredBeanNames != null) {
1110-
autowiredBeanNames.add(entry.getKey());
1125+
autowiredBeanNames.add(autowiredBeanName);
11111126
}
1112-
return entry.getValue();
1127+
return (instanceCandidate instanceof Class ?
1128+
descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
11131129
}
11141130
finally {
11151131
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
@@ -1122,9 +1138,8 @@ private Object resolveMultipleBeans(DependencyDescriptor descriptor, String bean
11221138
Class<?> type = descriptor.getDependencyType();
11231139
if (type.isArray()) {
11241140
Class<?> componentType = type.getComponentType();
1125-
DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor);
1126-
targetDesc.increaseNestingLevel();
1127-
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, targetDesc);
1141+
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
1142+
new MultiElementDependencyDescriptor(descriptor));
11281143
if (matchingBeans.isEmpty()) {
11291144
return null;
11301145
}
@@ -1143,9 +1158,8 @@ else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
11431158
if (elementType == null) {
11441159
return null;
11451160
}
1146-
DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor);
1147-
targetDesc.increaseNestingLevel();
1148-
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, targetDesc);
1161+
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
1162+
new MultiElementDependencyDescriptor(descriptor));
11491163
if (matchingBeans.isEmpty()) {
11501164
return null;
11511165
}
@@ -1168,9 +1182,8 @@ else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
11681182
if (valueType == null) {
11691183
return null;
11701184
}
1171-
DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor);
1172-
targetDesc.increaseNestingLevel();
1173-
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, targetDesc);
1185+
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
1186+
new MultiElementDependencyDescriptor(descriptor));
11741187
if (matchingBeans.isEmpty()) {
11751188
return null;
11761189
}
@@ -1239,79 +1252,94 @@ protected Map<String, Object> findAutowireCandidates(
12391252
}
12401253
for (String candidateName : candidateNames) {
12411254
if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
1242-
result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
1255+
addCandidateEntry(result, candidateName, descriptor, requiredType);
12431256
}
12441257
}
12451258
if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
12461259
// Consider fallback matches if the first pass failed to find anything...
12471260
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
12481261
for (String candidateName : candidateNames) {
12491262
if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
1250-
result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
1263+
addCandidateEntry(result, candidateName, descriptor, requiredType);
12511264
}
12521265
}
12531266
if (result.isEmpty()) {
12541267
// Consider self references before as a final pass
12551268
for (String candidateName : candidateNames) {
12561269
if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
1257-
result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
1270+
addCandidateEntry(result, candidateName, descriptor, requiredType);
12581271
}
12591272
}
12601273
}
12611274
}
12621275
return result;
12631276
}
12641277

1278+
/**
1279+
* Add an entry to the candidate map: a bean instance if available or just the resolved
1280+
* type, preventing early bean initialization ahead of primary candidate selection.
1281+
*/
1282+
private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
1283+
DependencyDescriptor descriptor, Class<?> requiredType) {
1284+
1285+
if (descriptor instanceof MultiElementDependencyDescriptor || containsSingleton(candidateName)) {
1286+
candidates.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
1287+
}
1288+
else {
1289+
candidates.put(candidateName, getType(candidateName));
1290+
}
1291+
}
1292+
12651293
/**
12661294
* Determine the autowire candidate in the given set of beans.
12671295
* <p>Looks for {@code @Primary} and {@code @Priority} (in that order).
1268-
* @param candidateBeans a Map of candidate names and candidate instances
1296+
* @param candidates a Map of candidate names and candidate instances
12691297
* that match the required type, as returned by {@link #findAutowireCandidates}
12701298
* @param descriptor the target dependency to match against
12711299
* @return the name of the autowire candidate, or {@code null} if none found
12721300
*/
1273-
protected String determineAutowireCandidate(Map<String, Object> candidateBeans, DependencyDescriptor descriptor) {
1301+
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
12741302
Class<?> requiredType = descriptor.getDependencyType();
1275-
String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);
1303+
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
12761304
if (primaryCandidate != null) {
12771305
return primaryCandidate;
12781306
}
1279-
String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);
1307+
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
12801308
if (priorityCandidate != null) {
12811309
return priorityCandidate;
12821310
}
12831311
// Fallback
1284-
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
1285-
String candidateBeanName = entry.getKey();
1312+
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
1313+
String candidateName = entry.getKey();
12861314
Object beanInstance = entry.getValue();
12871315
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
1288-
matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {
1289-
return candidateBeanName;
1316+
matchesBeanName(candidateName, descriptor.getDependencyName())) {
1317+
return candidateName;
12901318
}
12911319
}
12921320
return null;
12931321
}
12941322

12951323
/**
12961324
* Determine the primary candidate in the given set of beans.
1297-
* @param candidateBeans a Map of candidate names and candidate instances
1298-
* that match the required type
1325+
* @param candidates a Map of candidate names and candidate instances
1326+
* (or candidate classes if not created yet) that match the required type
12991327
* @param requiredType the target dependency type to match against
13001328
* @return the name of the primary candidate, or {@code null} if none found
13011329
* @see #isPrimary(String, Object)
13021330
*/
1303-
protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) {
1331+
protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
13041332
String primaryBeanName = null;
1305-
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
1333+
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
13061334
String candidateBeanName = entry.getKey();
13071335
Object beanInstance = entry.getValue();
13081336
if (isPrimary(candidateBeanName, beanInstance)) {
13091337
if (primaryBeanName != null) {
13101338
boolean candidateLocal = containsBeanDefinition(candidateBeanName);
13111339
boolean primaryLocal = containsBeanDefinition(primaryBeanName);
13121340
if (candidateLocal && primaryLocal) {
1313-
throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
1314-
"more than one 'primary' bean found among candidates: " + candidateBeans.keySet());
1341+
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
1342+
"more than one 'primary' bean found among candidates: " + candidates.keySet());
13151343
}
13161344
else if (candidateLocal) {
13171345
primaryBeanName = candidateBeanName;
@@ -1326,29 +1354,30 @@ else if (candidateLocal) {
13261354
}
13271355

13281356
/**
1329-
* Determine the candidate with the highest priority in the given set of beans. As
1330-
* defined by the {@link org.springframework.core.Ordered} interface, the lowest
1331-
* value has the highest priority.
1332-
* @param candidateBeans a Map of candidate names and candidate instances
1333-
* that match the required type
1357+
* Determine the candidate with the highest priority in the given set of beans.
1358+
* <p>Based on {@code @javax.annotation.Priority}. As defined by the related
1359+
* {@link org.springframework.core.Ordered} interface, the lowest value has
1360+
* the highest priority.
1361+
* @param candidates a Map of candidate names and candidate instances
1362+
* (or candidate classes if not created yet) that match the required type
13341363
* @param requiredType the target dependency type to match against
13351364
* @return the name of the candidate with the highest priority,
13361365
* or {@code null} if none found
13371366
* @see #getPriority(Object)
13381367
*/
1339-
protected String determineHighestPriorityCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) {
1368+
protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
13401369
String highestPriorityBeanName = null;
13411370
Integer highestPriority = null;
1342-
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
1371+
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
13431372
String candidateBeanName = entry.getKey();
13441373
Object beanInstance = entry.getValue();
13451374
Integer candidatePriority = getPriority(beanInstance);
13461375
if (candidatePriority != null) {
13471376
if (highestPriorityBeanName != null) {
13481377
if (candidatePriority.equals(highestPriority)) {
1349-
throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
1350-
"Multiple beans found with the same priority ('" + highestPriority + "') " +
1351-
"among candidates: " + candidateBeans.keySet());
1378+
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
1379+
"Multiple beans found with the same priority ('" + highestPriority +
1380+
"') among candidates: " + candidates.keySet());
13521381
}
13531382
else if (candidatePriority < highestPriority) {
13541383
highestPriorityBeanName = candidateBeanName;
@@ -1529,7 +1558,7 @@ private Object readResolve() {
15291558
private class OptionalDependencyFactory {
15301559

15311560
public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) {
1532-
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
1561+
DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
15331562
@Override
15341563
public boolean isRequired() {
15351564
return false;
@@ -1540,7 +1569,6 @@ public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFacto
15401569
super.resolveCandidate(beanName, requiredType, beanFactory));
15411570
}
15421571
};
1543-
descriptorToUse.increaseNestingLevel();
15441572
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
15451573
}
15461574
}
@@ -1558,9 +1586,8 @@ private class DependencyObjectProvider implements ObjectProvider<Object>, Serial
15581586
private final String beanName;
15591587

15601588
public DependencyObjectProvider(DependencyDescriptor descriptor, String beanName) {
1561-
this.descriptor = new DependencyDescriptor(descriptor);
1562-
this.descriptor.increaseNestingLevel();
1563-
this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass);
1589+
this.descriptor = new NestedDependencyDescriptor(descriptor);
1590+
this.optional = (this.descriptor.getDependencyType() == javaUtilOptionalClass);
15641591
this.beanName = beanName;
15651592
}
15661593

@@ -1676,7 +1703,7 @@ public Object getOrderSource(Object obj) {
16761703
if (beanDefinition == null) {
16771704
return null;
16781705
}
1679-
List<Object> sources = new ArrayList<Object>();
1706+
List<Object> sources = new ArrayList<Object>(2);
16801707
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
16811708
if (factoryMethod != null) {
16821709
sources.add(factoryMethod);
@@ -1699,4 +1726,21 @@ private RootBeanDefinition getRootBeanDefinition(String beanName) {
16991726
}
17001727
}
17011728

1729+
1730+
private static class NestedDependencyDescriptor extends DependencyDescriptor {
1731+
1732+
public NestedDependencyDescriptor(DependencyDescriptor original) {
1733+
super(original);
1734+
increaseNestingLevel();
1735+
}
1736+
}
1737+
1738+
1739+
private static class MultiElementDependencyDescriptor extends NestedDependencyDescriptor {
1740+
1741+
public MultiElementDependencyDescriptor(DependencyDescriptor original) {
1742+
super(original);
1743+
}
1744+
}
1745+
17021746
}

spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,12 +1435,14 @@ public void testGetBeanByTypeWithAmbiguity() {
14351435
public void testGetBeanByTypeWithPrimary() throws Exception {
14361436
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
14371437
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
1438+
bd1.setLazyInit(true);
14381439
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
14391440
bd2.setPrimary(true);
14401441
lbf.registerBeanDefinition("bd1", bd1);
14411442
lbf.registerBeanDefinition("bd2", bd2);
14421443
TestBean bean = lbf.getBean(TestBean.class);
14431444
assertThat(bean.getBeanName(), equalTo("bd2"));
1445+
assertFalse(lbf.containsSingleton("bd1"));
14441446
}
14451447

14461448
@Test
@@ -1760,24 +1762,6 @@ public void testAutowireBeanByTypeWithTwoMatches() {
17601762
}
17611763
}
17621764

1763-
@Test
1764-
public void testAutowireBeanByTypeWithTwoMatchesAndParameterNameDiscovery() {
1765-
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
1766-
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
1767-
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
1768-
lbf.registerBeanDefinition("test", bd);
1769-
lbf.registerBeanDefinition("spouse", bd2);
1770-
try {
1771-
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
1772-
fail("Should have thrown UnsatisfiedDependencyException");
1773-
}
1774-
catch (UnsatisfiedDependencyException ex) {
1775-
// expected
1776-
assertTrue(ex.getMessage().contains("test"));
1777-
assertTrue(ex.getMessage().contains("spouse"));
1778-
}
1779-
}
1780-
17811765
@Test
17821766
public void testAutowireBeanByTypeWithDependencyCheck() {
17831767
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();

0 commit comments

Comments
 (0)