Skip to content

Commit 0408bb5

Browse files
jhoellerpinguin3245678
authored andcommitted
Consistently resolve unique default candidate bean
Closes spring-projectsgh-34432 Signed-off-by: Vincent Potucek <[email protected]>
1 parent 733cbc5 commit 0408bb5

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.beans.BeanMetadataElement;
3838
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3939
import org.springframework.beans.factory.ObjectFactory;
40+
import org.springframework.beans.factory.config.BeanDefinition;
4041
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
4142
import org.springframework.beans.factory.config.TypedStringValue;
4243
import org.springframework.util.Assert;
@@ -280,6 +281,25 @@ public static boolean isAutowireCandidate(ConfigurableBeanFactory beanFactory, S
280281
}
281282
}
282283

284+
/**
285+
* Check the default-candidate status for the specified bean.
286+
* @param beanFactory the bean factory
287+
* @param beanName the name of the bean to check
288+
* @return whether the specified bean qualifies as a default candidate
289+
* @since 6.2.4
290+
* @see AbstractBeanDefinition#isDefaultCandidate()
291+
*/
292+
public static boolean isDefaultCandidate(ConfigurableBeanFactory beanFactory, String beanName) {
293+
try {
294+
BeanDefinition mbd = beanFactory.getMergedBeanDefinition(beanName);
295+
return (!(mbd instanceof AbstractBeanDefinition abd) || abd.isDefaultCandidate());
296+
}
297+
catch (NoSuchBeanDefinitionException ex) {
298+
// A manually registered singleton instance not backed by a BeanDefinition.
299+
return true;
300+
}
301+
}
302+
283303

284304
/**
285305
* Reflective {@link InvocationHandler} for lazy access to the current target object.

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,9 @@ else if (candidateNames.length > 1) {
14791479
if (candidateName == null) {
14801480
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
14811481
}
1482+
if (candidateName == null) {
1483+
candidateName = determineDefaultCandidate(candidates);
1484+
}
14821485
if (candidateName != null) {
14831486
Object beanInstance = candidates.get(candidateName);
14841487
if (beanInstance == null) {
@@ -1939,7 +1942,12 @@ else if (containsSingleton(candidateName) ||
19391942
if (priorityCandidate != null) {
19401943
return priorityCandidate;
19411944
}
1942-
// Step 4: pick directly registered dependency
1945+
// Step 4: pick unique default-candidate
1946+
String defaultCandidate = determineDefaultCandidate(candidates);
1947+
if (defaultCandidate != null) {
1948+
return defaultCandidate;
1949+
}
1950+
// Step 5: pick directly registered dependency
19431951
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
19441952
String candidateName = entry.getKey();
19451953
Object beanInstance = entry.getValue();
@@ -2097,6 +2105,28 @@ private boolean isFallback(String beanName) {
20972105
return null;
20982106
}
20992107

2108+
/**
2109+
* Return a unique "default-candidate" among remaining non-default candidates.
2110+
* @param candidates a Map of candidate names and candidate instances
2111+
* (or candidate classes if not created yet) that match the required type
2112+
* @return the name of the default candidate, or {@code null} if none found
2113+
* @since 6.2.4
2114+
* @see AbstractBeanDefinition#isDefaultCandidate()
2115+
*/
2116+
@Nullable
2117+
private String determineDefaultCandidate(Map<String, Object> candidates) {
2118+
String defaultBeanName = null;
2119+
for (String candidateBeanName : candidates.keySet()) {
2120+
if (AutowireUtils.isDefaultCandidate(this, candidateBeanName)) {
2121+
if (defaultBeanName != null) {
2122+
return null;
2123+
}
2124+
defaultBeanName = candidateBeanName;
2125+
}
2126+
}
2127+
return defaultBeanName;
2128+
}
2129+
21002130
/**
21012131
* Determine whether the given candidate name matches the bean name or the aliases
21022132
* stored in this bean definition.

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,12 +1658,40 @@ void getBeanByTypeWithPrimary() {
16581658
bd2.setPrimary(true);
16591659
lbf.registerBeanDefinition("bd1", bd1);
16601660
lbf.registerBeanDefinition("bd2", bd2);
1661+
lbf.registerSingleton("bd3", new TestBean());
16611662

16621663
TestBean bean = lbf.getBean(TestBean.class);
16631664
assertThat(bean.getBeanName()).isEqualTo("bd2");
16641665
assertThat(lbf.containsSingleton("bd1")).isFalse();
16651666
}
16661667

1668+
@Test
1669+
void getBeanByTypeWithUniqueNonDefaultDefinition() {
1670+
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
1671+
bd1.setDefaultCandidate(false);
1672+
bd1.setLazyInit(true);
1673+
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
1674+
lbf.registerBeanDefinition("bd1", bd1);
1675+
lbf.registerBeanDefinition("bd2", bd2);
1676+
1677+
TestBean bean = lbf.getBean(TestBean.class);
1678+
assertThat(bean.getBeanName()).isEqualTo("bd2");
1679+
assertThat(lbf.containsSingleton("bd1")).isFalse();
1680+
}
1681+
1682+
@Test
1683+
void getBeanByTypeWithUniqueNonDefaultSingleton() {
1684+
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
1685+
bd1.setDefaultCandidate(false);
1686+
bd1.setLazyInit(true);
1687+
lbf.registerBeanDefinition("bd1", bd1);
1688+
lbf.registerSingleton("bd2", new TestBean());
1689+
1690+
TestBean bean = lbf.getBean(TestBean.class);
1691+
assertThat(bean.getBeanName()).isNull();
1692+
assertThat(lbf.containsSingleton("bd1")).isFalse();
1693+
}
1694+
16671695
@Test
16681696
@SuppressWarnings("rawtypes")
16691697
void getFactoryBeanByTypeWithPrimary() {

0 commit comments

Comments
 (0)