Restore original override behavior when override allowed
Closes gh-33920
This commit is contained in:
parent
68d6cb9d35
commit
66da5d7ab9
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -54,6 +54,22 @@ public class BeanDefinitionOverrideException extends BeanDefinitionStoreExceptio
|
|||
this.existingDefinition = existingDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BeanDefinitionOverrideException for the given new and existing definition.
|
||||
* @param beanName the name of the bean
|
||||
* @param beanDefinition the newly registered bean definition
|
||||
* @param existingDefinition the existing bean definition for the same name
|
||||
* @param msg the detail message to include
|
||||
* @since 6.2.1
|
||||
*/
|
||||
public BeanDefinitionOverrideException(
|
||||
String beanName, BeanDefinition beanDefinition, BeanDefinition existingDefinition, String msg) {
|
||||
|
||||
super(beanDefinition.getResourceDescription(), beanName, msg);
|
||||
this.beanDefinition = beanDefinition;
|
||||
this.existingDefinition = existingDefinition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the description of the resource that the bean definition came from.
|
||||
|
|
|
@ -36,10 +36,10 @@ import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
|
|||
import org.springframework.beans.factory.parsing.SourceExtractor;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionOverrideException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
|
||||
|
@ -297,13 +297,21 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
return false;
|
||||
}
|
||||
BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
|
||||
ConfigurationClass configClass = beanMethod.getConfigurationClass();
|
||||
|
||||
// If the bean method is an overloaded case on the same configuration class,
|
||||
// preserve the existing bean definition and mark it as overloaded.
|
||||
if (existingBeanDef instanceof ConfigurationClassBeanDefinition ccbd) {
|
||||
if (ccbd.getMetadata().getClassName().equals(beanMethod.getConfigurationClass().getMetadata().getClassName()) &&
|
||||
ccbd.getFactoryMethodMetadata().getMethodName().equals(beanMethod.getMetadata().getMethodName())) {
|
||||
if (ccbd.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
|
||||
if (ccbd.getFactoryMethodMetadata().getMethodName().equals(beanMethod.getMetadata().getMethodName())) {
|
||||
ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
|
||||
}
|
||||
else if (!this.registry.isBeanDefinitionOverridable(beanName)) {
|
||||
throw new BeanDefinitionOverrideException(beanName,
|
||||
new ConfigurationClassBeanDefinition(configClass, beanMethod.getMetadata(), beanName),
|
||||
existingBeanDef,
|
||||
"@Bean method override with same bean name but different method name: " + existingBeanDef);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -329,9 +337,11 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
// At this point, it's a top-level override (probably XML), just having been parsed
|
||||
// before configuration class processing kicks in...
|
||||
if (this.registry instanceof DefaultListableBeanFactory dlbf && !dlbf.isBeanDefinitionOverridable(beanName)) {
|
||||
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
|
||||
beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
|
||||
if (!this.registry.isBeanDefinitionOverridable(beanName)) {
|
||||
throw new BeanDefinitionOverrideException(beanName,
|
||||
new ConfigurationClassBeanDefinition(configClass, beanMethod.getMetadata(), beanName),
|
||||
existingBeanDef,
|
||||
"@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
|
||||
|
|
|
@ -110,7 +110,7 @@ class ConfigurationClassProcessingTests {
|
|||
|
||||
private void aliasesAreRespected(Class<?> testClass, Supplier<TestBean> testBeanSupplier, String beanName) {
|
||||
TestBean testBean = testBeanSupplier.get();
|
||||
BeanFactory factory = initBeanFactory(testClass);
|
||||
BeanFactory factory = initBeanFactory(false, testClass);
|
||||
|
||||
assertThat(factory.getBean(beanName)).isSameAs(testBean);
|
||||
Arrays.stream(factory.getAliases(beanName)).map(factory::getBean).forEach(alias -> assertThat(alias).isSameAs(testBean));
|
||||
|
@ -141,30 +141,30 @@ class ConfigurationClassProcessingTests {
|
|||
@Test
|
||||
void finalBeanMethod() {
|
||||
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() ->
|
||||
initBeanFactory(ConfigWithFinalBean.class));
|
||||
initBeanFactory(false, ConfigWithFinalBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void finalBeanMethodWithoutProxy() {
|
||||
initBeanFactory(ConfigWithFinalBeanWithoutProxy.class);
|
||||
initBeanFactory(false, ConfigWithFinalBeanWithoutProxy.class);
|
||||
}
|
||||
|
||||
@Test // gh-31007
|
||||
void voidBeanMethod() {
|
||||
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() ->
|
||||
initBeanFactory(ConfigWithVoidBean.class));
|
||||
initBeanFactory(false, ConfigWithVoidBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void simplestPossibleConfig() {
|
||||
BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class);
|
||||
BeanFactory factory = initBeanFactory(false, SimplestPossibleConfig.class);
|
||||
String stringBean = factory.getBean("stringBean", String.class);
|
||||
assertThat(stringBean).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configWithObjectReturnType() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
|
||||
BeanFactory factory = initBeanFactory(false, ConfigWithNonSpecificReturnTypes.class);
|
||||
assertThat(factory.getType("stringBean")).isEqualTo(Object.class);
|
||||
assertThat(factory.isTypeMatch("stringBean", String.class)).isFalse();
|
||||
String stringBean = factory.getBean("stringBean", String.class);
|
||||
|
@ -173,7 +173,7 @@ class ConfigurationClassProcessingTests {
|
|||
|
||||
@Test
|
||||
void configWithFactoryBeanReturnType() {
|
||||
ListableBeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
|
||||
ListableBeanFactory factory = initBeanFactory(false, ConfigWithNonSpecificReturnTypes.class);
|
||||
assertThat(factory.getType("factoryBean")).isEqualTo(List.class);
|
||||
assertThat(factory.isTypeMatch("factoryBean", List.class)).isTrue();
|
||||
assertThat(factory.getType("&factoryBean")).isEqualTo(FactoryBean.class);
|
||||
|
@ -201,7 +201,7 @@ class ConfigurationClassProcessingTests {
|
|||
|
||||
@Test
|
||||
void configurationWithPrototypeScopedBeans() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
||||
BeanFactory factory = initBeanFactory(false, ConfigWithPrototypeBean.class);
|
||||
|
||||
TestBean foo = factory.getBean("foo", TestBean.class);
|
||||
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
||||
|
@ -213,7 +213,7 @@ class ConfigurationClassProcessingTests {
|
|||
|
||||
@Test
|
||||
void configurationWithNullReference() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithNullReference.class);
|
||||
BeanFactory factory = initBeanFactory(false, ConfigWithNullReference.class);
|
||||
|
||||
TestBean foo = factory.getBean("foo", TestBean.class);
|
||||
assertThat(factory.getBean("bar")).isEqualTo(null);
|
||||
|
@ -223,7 +223,15 @@ class ConfigurationClassProcessingTests {
|
|||
@Test // gh-33330
|
||||
void configurationWithMethodNameMismatch() {
|
||||
assertThatExceptionOfType(BeanDefinitionOverrideException.class)
|
||||
.isThrownBy(() -> initBeanFactory(ConfigWithMethodNameMismatch.class));
|
||||
.isThrownBy(() -> initBeanFactory(false, ConfigWithMethodNameMismatch.class));
|
||||
}
|
||||
|
||||
@Test // gh-33920
|
||||
void configurationWithMethodNameMismatchAndOverridingAllowed() {
|
||||
BeanFactory factory = initBeanFactory(true, ConfigWithMethodNameMismatch.class);
|
||||
|
||||
SpousyTestBean foo = factory.getBean("foo", SpousyTestBean.class);
|
||||
assertThat(foo.getName()).isEqualTo("foo1");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -353,13 +361,13 @@ class ConfigurationClassProcessingTests {
|
|||
* When complete, the factory is ready to service requests for any {@link Bean} methods
|
||||
* declared by {@code configClasses}.
|
||||
*/
|
||||
private DefaultListableBeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||
private DefaultListableBeanFactory initBeanFactory(boolean allowOverriding, Class<?>... configClasses) {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
for (Class<?> configClass : configClasses) {
|
||||
String configBeanName = configClass.getName();
|
||||
factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass));
|
||||
}
|
||||
factory.setAllowBeanDefinitionOverriding(false);
|
||||
factory.setAllowBeanDefinitionOverriding(allowOverriding);
|
||||
ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor();
|
||||
ccpp.postProcessBeanDefinitionRegistry(factory);
|
||||
ccpp.postProcessBeanFactory(factory);
|
||||
|
@ -537,12 +545,12 @@ class ConfigurationClassProcessingTests {
|
|||
@Configuration
|
||||
static class ConfigWithMethodNameMismatch {
|
||||
|
||||
@Bean(name = "foo") public TestBean foo() {
|
||||
return new SpousyTestBean("foo");
|
||||
@Bean(name = "foo") public TestBean foo1() {
|
||||
return new SpousyTestBean("foo1");
|
||||
}
|
||||
|
||||
@Bean(name = "foo") public TestBean fooX() {
|
||||
return new SpousyTestBean("fooX");
|
||||
@Bean(name = "foo") public TestBean foo2() {
|
||||
return new SpousyTestBean("foo2");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue