@@ -1357,12 +1357,15 @@ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable Str
1357
1357
1358
1358
InjectionPoint previousInjectionPoint = ConstructorResolver .setCurrentInjectionPoint (descriptor );
1359
1359
try {
1360
+ // Step 1: pre-resolved shortcut for single bean match, e.g. from @Autowired
1360
1361
Object shortcut = descriptor .resolveShortcut (this );
1361
1362
if (shortcut != null ) {
1362
1363
return shortcut ;
1363
1364
}
1364
1365
1365
1366
Class <?> type = descriptor .getDependencyType ();
1367
+
1368
+ // Step 2: pre-defined value or expression, e.g. from @Value
1366
1369
Object value = getAutowireCandidateResolver ().getSuggestedValue (descriptor );
1367
1370
if (value != null ) {
1368
1371
if (value instanceof String strValue ) {
@@ -1383,13 +1386,20 @@ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable Str
1383
1386
}
1384
1387
}
1385
1388
1389
+ // Step 3a: multiple beans as stream / array / standard collection / plain map
1386
1390
Object multipleBeans = resolveMultipleBeans (descriptor , beanName , autowiredBeanNames , typeConverter );
1387
1391
if (multipleBeans != null ) {
1388
1392
return multipleBeans ;
1389
1393
}
1390
-
1394
+ // Step 3b: direct bean matches, possibly direct beans of type Collection / Map
1391
1395
Map <String , Object > matchingBeans = findAutowireCandidates (beanName , type , descriptor );
1392
1396
if (matchingBeans .isEmpty ()) {
1397
+ // Step 3c (fallback): custom Collection / Map declarations for collecting multiple beans
1398
+ multipleBeans = resolveMultipleBeansFallback (descriptor , beanName , autowiredBeanNames , typeConverter );
1399
+ if (multipleBeans != null ) {
1400
+ return multipleBeans ;
1401
+ }
1402
+ // Raise exception if nothing found for required injection point
1393
1403
if (isRequired (descriptor )) {
1394
1404
raiseNoMatchingBeanFound (type , descriptor .getResolvableType (), descriptor );
1395
1405
}
@@ -1399,10 +1409,12 @@ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable Str
1399
1409
String autowiredBeanName ;
1400
1410
Object instanceCandidate ;
1401
1411
1412
+ // Step 4: determine single candidate
1402
1413
if (matchingBeans .size () > 1 ) {
1403
1414
autowiredBeanName = determineAutowireCandidate (matchingBeans , descriptor );
1404
1415
if (autowiredBeanName == null ) {
1405
- if (isRequired (descriptor ) || !indicatesMultipleBeans (type )) {
1416
+ if (isRequired (descriptor ) || !indicatesArrayCollectionOrMap (type )) {
1417
+ // Raise exception if no clear match found for required injection point
1406
1418
return descriptor .resolveNotUnique (descriptor .getResolvableType (), matchingBeans );
1407
1419
}
1408
1420
else {
@@ -1421,6 +1433,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable Str
1421
1433
instanceCandidate = entry .getValue ();
1422
1434
}
1423
1435
1436
+ // Step 5: validate single result
1424
1437
if (autowiredBeanNames != null ) {
1425
1438
autowiredBeanNames .add (autowiredBeanName );
1426
1439
}
@@ -1430,6 +1443,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable Str
1430
1443
Object result = instanceCandidate ;
1431
1444
if (result instanceof NullBean ) {
1432
1445
if (isRequired (descriptor )) {
1446
+ // Raise exception if null encountered for required injection point
1433
1447
raiseNoMatchingBeanFound (type , descriptor .getResolvableType (), descriptor );
1434
1448
}
1435
1449
result = null ;
@@ -1491,63 +1505,92 @@ else if (type.isArray()) {
1491
1505
}
1492
1506
return result ;
1493
1507
}
1494
- else if (Collection .class .isAssignableFrom (type ) && type .isInterface ()) {
1495
- Class <?> elementType = descriptor .getResolvableType ().asCollection ().resolveGeneric ();
1496
- if (elementType == null ) {
1497
- return null ;
1498
- }
1499
- Map <String , Object > matchingBeans = findAutowireCandidates (beanName , elementType ,
1500
- new MultiElementDescriptor (descriptor ));
1501
- if (matchingBeans .isEmpty ()) {
1502
- return null ;
1503
- }
1504
- if (autowiredBeanNames != null ) {
1505
- autowiredBeanNames .addAll (matchingBeans .keySet ());
1506
- }
1507
- TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter ());
1508
- Object result = converter .convertIfNecessary (matchingBeans .values (), type );
1509
- if (result instanceof List <?> list && list .size () > 1 ) {
1510
- Comparator <Object > comparator = adaptDependencyComparator (matchingBeans );
1511
- if (comparator != null ) {
1512
- list .sort (comparator );
1513
- }
1514
- }
1515
- return result ;
1508
+ else if (Collection .class == type || Set .class == type || List .class == type ) {
1509
+ return resolveMultipleBeanCollection (descriptor , beanName , autowiredBeanNames , typeConverter );
1516
1510
}
1517
1511
else if (Map .class == type ) {
1518
- ResolvableType mapType = descriptor .getResolvableType ().asMap ();
1519
- Class <?> keyType = mapType .resolveGeneric (0 );
1520
- if (String .class != keyType ) {
1521
- return null ;
1522
- }
1523
- Class <?> valueType = mapType .resolveGeneric (1 );
1524
- if (valueType == null ) {
1525
- return null ;
1526
- }
1527
- Map <String , Object > matchingBeans = findAutowireCandidates (beanName , valueType ,
1528
- new MultiElementDescriptor (descriptor ));
1529
- if (matchingBeans .isEmpty ()) {
1530
- return null ;
1531
- }
1532
- if (autowiredBeanNames != null ) {
1533
- autowiredBeanNames .addAll (matchingBeans .keySet ());
1534
- }
1535
- return matchingBeans ;
1512
+ return resolveMultipleBeanMap (descriptor , beanName , autowiredBeanNames , typeConverter );
1536
1513
}
1537
- else {
1514
+ return null ;
1515
+ }
1516
+
1517
+
1518
+ @ Nullable
1519
+ private Object resolveMultipleBeansFallback (DependencyDescriptor descriptor , @ Nullable String beanName ,
1520
+ @ Nullable Set <String > autowiredBeanNames , @ Nullable TypeConverter typeConverter ) {
1521
+
1522
+ Class <?> type = descriptor .getDependencyType ();
1523
+
1524
+ if (Collection .class .isAssignableFrom (type ) && type .isInterface ()) {
1525
+ return resolveMultipleBeanCollection (descriptor , beanName , autowiredBeanNames , typeConverter );
1526
+ }
1527
+ else if (Map .class .isAssignableFrom (type ) && type .isInterface ()) {
1528
+ return resolveMultipleBeanMap (descriptor , beanName , autowiredBeanNames , typeConverter );
1529
+ }
1530
+ return null ;
1531
+ }
1532
+
1533
+ @ Nullable
1534
+ private Object resolveMultipleBeanCollection (DependencyDescriptor descriptor , @ Nullable String beanName ,
1535
+ @ Nullable Set <String > autowiredBeanNames , @ Nullable TypeConverter typeConverter ) {
1536
+
1537
+ Class <?> elementType = descriptor .getResolvableType ().asCollection ().resolveGeneric ();
1538
+ if (elementType == null ) {
1539
+ return null ;
1540
+ }
1541
+ Map <String , Object > matchingBeans = findAutowireCandidates (beanName , elementType ,
1542
+ new MultiElementDescriptor (descriptor ));
1543
+ if (matchingBeans .isEmpty ()) {
1538
1544
return null ;
1539
1545
}
1546
+ if (autowiredBeanNames != null ) {
1547
+ autowiredBeanNames .addAll (matchingBeans .keySet ());
1548
+ }
1549
+ TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter ());
1550
+ Object result = converter .convertIfNecessary (matchingBeans .values (), descriptor .getDependencyType ());
1551
+ if (result instanceof List <?> list && list .size () > 1 ) {
1552
+ Comparator <Object > comparator = adaptDependencyComparator (matchingBeans );
1553
+ if (comparator != null ) {
1554
+ list .sort (comparator );
1555
+ }
1556
+ }
1557
+ return result ;
1540
1558
}
1541
1559
1542
- private boolean isRequired (DependencyDescriptor descriptor ) {
1543
- return getAutowireCandidateResolver ().isRequired (descriptor );
1560
+ @ Nullable
1561
+ private Object resolveMultipleBeanMap (DependencyDescriptor descriptor , @ Nullable String beanName ,
1562
+ @ Nullable Set <String > autowiredBeanNames , @ Nullable TypeConverter typeConverter ) {
1563
+
1564
+ ResolvableType mapType = descriptor .getResolvableType ().asMap ();
1565
+ Class <?> keyType = mapType .resolveGeneric (0 );
1566
+ if (String .class != keyType ) {
1567
+ return null ;
1568
+ }
1569
+ Class <?> valueType = mapType .resolveGeneric (1 );
1570
+ if (valueType == null ) {
1571
+ return null ;
1572
+ }
1573
+ Map <String , Object > matchingBeans = findAutowireCandidates (beanName , valueType ,
1574
+ new MultiElementDescriptor (descriptor ));
1575
+ if (matchingBeans .isEmpty ()) {
1576
+ return null ;
1577
+ }
1578
+ if (autowiredBeanNames != null ) {
1579
+ autowiredBeanNames .addAll (matchingBeans .keySet ());
1580
+ }
1581
+ TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter ());
1582
+ return converter .convertIfNecessary (matchingBeans , descriptor .getDependencyType ());
1544
1583
}
1545
1584
1546
- private boolean indicatesMultipleBeans (Class <?> type ) {
1585
+ private boolean indicatesArrayCollectionOrMap (Class <?> type ) {
1547
1586
return (type .isArray () || (type .isInterface () &&
1548
1587
(Collection .class .isAssignableFrom (type ) || Map .class .isAssignableFrom (type ))));
1549
1588
}
1550
1589
1590
+ private boolean isRequired (DependencyDescriptor descriptor ) {
1591
+ return getAutowireCandidateResolver ().isRequired (descriptor );
1592
+ }
1593
+
1551
1594
@ Nullable
1552
1595
private Comparator <Object > adaptDependencyComparator (Map <String , ?> matchingBeans ) {
1553
1596
Comparator <Object > comparator = getDependencyComparator ();
@@ -1609,7 +1652,7 @@ protected Map<String, Object> findAutowireCandidates(
1609
1652
}
1610
1653
}
1611
1654
if (result .isEmpty ()) {
1612
- boolean multiple = indicatesMultipleBeans (requiredType );
1655
+ boolean multiple = indicatesArrayCollectionOrMap (requiredType );
1613
1656
// Consider fallback matches if the first pass failed to find anything...
1614
1657
DependencyDescriptor fallbackDescriptor = descriptor .forFallbackMatch ();
1615
1658
for (String candidate : candidateNames ) {
@@ -1675,7 +1718,7 @@ protected String determineAutowireCandidate(Map<String, Object> candidates, Depe
1675
1718
if (priorityCandidate != null ) {
1676
1719
return priorityCandidate ;
1677
1720
}
1678
- // Fallback
1721
+ // Fallback: pick directly registered dependency or qualified bean name match
1679
1722
for (Map .Entry <String , Object > entry : candidates .entrySet ()) {
1680
1723
String candidateName = entry .getKey ();
1681
1724
Object beanInstance = entry .getValue ();
@@ -2094,7 +2137,6 @@ public Object getIfUnique() throws BeansException {
2094
2137
public boolean isRequired () {
2095
2138
return false ;
2096
2139
}
2097
-
2098
2140
@ Override
2099
2141
@ Nullable
2100
2142
public Object resolveNotUnique (ResolvableType type , Map <String , Object > matchingBeans ) {
0 commit comments