Merge branch '6.2.x'

This commit is contained in:
Juergen Hoeller 2025-02-18 13:14:38 +01:00
commit 2e4fbd1ff3
7 changed files with 106 additions and 9 deletions

View File

@ -37,6 +37,7 @@ import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.util.Assert;
@ -280,6 +281,25 @@ abstract class AutowireUtils {
}
}
/**
* Check the default-candidate status for the specified bean.
* @param beanFactory the bean factory
* @param beanName the name of the bean to check
* @return whether the specified bean qualifies as a default candidate
* @since 6.2.4
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
public static boolean isDefaultCandidate(ConfigurableBeanFactory beanFactory, String beanName) {
try {
BeanDefinition mbd = beanFactory.getMergedBeanDefinition(beanName);
return (!(mbd instanceof AbstractBeanDefinition abd) || abd.isDefaultCandidate());
}
catch (NoSuchBeanDefinitionException ex) {
// A manually registered singleton instance not backed by a BeanDefinition.
return true;
}
}
/**
* Reflective {@link InvocationHandler} for lazy access to the current target object.

View File

@ -1479,6 +1479,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (candidateName == null) {
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
if (candidateName == null) {
candidateName = determineDefaultCandidate(candidates);
}
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
if (beanInstance == null) {
@ -1939,7 +1942,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (priorityCandidate != null) {
return priorityCandidate;
}
// Step 4: pick directly registered dependency
// Step 4: pick unique default-candidate
String defaultCandidate = determineDefaultCandidate(candidates);
if (defaultCandidate != null) {
return defaultCandidate;
}
// Step 5: pick directly registered dependency
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
@ -2097,6 +2105,28 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
/**
* Return a unique "default-candidate" among remaining non-default candidates.
* @param candidates a Map of candidate names and candidate instances
* (or candidate classes if not created yet) that match the required type
* @return the name of the default candidate, or {@code null} if none found
* @since 6.2.4
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
@Nullable
private String determineDefaultCandidate(Map<String, Object> candidates) {
String defaultBeanName = null;
for (String candidateBeanName : candidates.keySet()) {
if (AutowireUtils.isDefaultCandidate(this, candidateBeanName)) {
if (defaultBeanName != null) {
return null;
}
defaultBeanName = candidateBeanName;
}
}
return defaultBeanName;
}
/**
* Determine whether the given candidate name matches the bean name or the aliases
* stored in this bean definition.

View File

@ -366,9 +366,19 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
try {
addSingleton(beanName, singletonObject);
}
catch (IllegalStateException ex) {
// Leniently accept same instance if implicitly appeared.
Object object = this.singletonObjects.get(beanName);
if (singletonObject != object) {
throw ex;
}
}
}
}
return singletonObject;
}

View File

@ -1658,12 +1658,40 @@ class DefaultListableBeanFactoryTests {
bd2.setPrimary(true);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerSingleton("bd3", new TestBean());
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonDefaultDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setDefaultCandidate(false);
bd1.setLazyInit(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonDefaultSingleton() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setDefaultCandidate(false);
bd1.setLazyInit(true);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerSingleton("bd2", new TestBean());
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isNull();
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
@SuppressWarnings("rawtypes")
void getFactoryBeanByTypeWithPrimary() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -50,10 +50,15 @@ class DefaultSingletonBeanRegistryTests {
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(tb2Flag.get()).isTrue();
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
TestBean tb3 = (TestBean) beanRegistry.getSingleton("tb3", () -> {
TestBean newTb = new TestBean();
beanRegistry.registerSingleton("tb3", newTb);
return newTb;
});
assertThat(beanRegistry.getSingleton("tb3")).isSameAs(tb3);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(3);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2", "tb3");
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isZero();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
@ -53,6 +54,7 @@ public class ExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionPa
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
@ -41,6 +42,7 @@ public class SchedulerBeanDefinitionParser extends AbstractSingleBeanDefinitionP
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
}