Skip to content

Commit 2e4fbd1

Browse files
committed
Merge branch '6.2.x'
2 parents bc82077 + d0ceefe commit 2e4fbd1

File tree

7 files changed

+106
-9
lines changed

7 files changed

+106
-9
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/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,18 @@ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
366366
}
367367
afterSingletonCreation(beanName);
368368
}
369+
369370
if (newSingleton) {
370-
addSingleton(beanName, singletonObject);
371+
try {
372+
addSingleton(beanName, singletonObject);
373+
}
374+
catch (IllegalStateException ex) {
375+
// Leniently accept same instance if implicitly appeared.
376+
Object object = this.singletonObjects.get(beanName);
377+
if (singletonObject != object) {
378+
throw ex;
379+
}
380+
}
371381
}
372382
}
373383
return singletonObject;

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() {

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 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.
@@ -50,10 +50,15 @@ void singletons() {
5050
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
5151
assertThat(tb2Flag.get()).isTrue();
5252

53-
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
54-
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
55-
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
56-
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
53+
TestBean tb3 = (TestBean) beanRegistry.getSingleton("tb3", () -> {
54+
TestBean newTb = new TestBean();
55+
beanRegistry.registerSingleton("tb3", newTb);
56+
return newTb;
57+
});
58+
assertThat(beanRegistry.getSingleton("tb3")).isSameAs(tb3);
59+
60+
assertThat(beanRegistry.getSingletonCount()).isEqualTo(3);
61+
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2", "tb3");
5762

5863
beanRegistry.destroySingletons();
5964
assertThat(beanRegistry.getSingletonCount()).isZero();

spring-context/src/main/java/org/springframework/scheduling/config/ExecutorBeanDefinitionParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 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.
@@ -18,6 +18,7 @@
1818

1919
import org.w3c.dom.Element;
2020

21+
import org.springframework.beans.factory.config.BeanDefinition;
2122
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2223
import org.springframework.beans.factory.support.RootBeanDefinition;
2324
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
@@ -53,6 +54,7 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit
5354
if (StringUtils.hasText(poolSize)) {
5455
builder.addPropertyValue("poolSize", poolSize);
5556
}
57+
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
5658
}
5759

5860
private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {

spring-context/src/main/java/org/springframework/scheduling/config/SchedulerBeanDefinitionParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2025 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.
@@ -18,6 +18,7 @@
1818

1919
import org.w3c.dom.Element;
2020

21+
import org.springframework.beans.factory.config.BeanDefinition;
2122
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2223
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
2324
import org.springframework.util.StringUtils;
@@ -41,6 +42,7 @@ protected void doParse(Element element, BeanDefinitionBuilder builder) {
4142
if (StringUtils.hasText(poolSize)) {
4243
builder.addPropertyValue("poolSize", poolSize);
4344
}
45+
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
4446
}
4547

4648
}

0 commit comments

Comments
 (0)