Relax singleton enforcement for Bean Overrides in the TestContext framework

In gh-33602, we introduced strict singleton enforcement for bean
overrides -- for example, for @⁠MockitoBean, @⁠TestBean, etc. However,
the use of BeanFactory#isSingleton(beanName) can result in a
BeanCreationException for certain beans, such as a Spring Data JPA
FactoryBean for a JpaRepository.

In light of that, this commit relaxes the singleton enforcement in
BeanOverrideBeanFactoryPostProcessor by only checking the result of
BeanDefinition#isSingleton() for existing bean definitions.

This commit also updates the Javadoc and reference documentation to
reflect the status quo.

See gh-33602
Closes gh-33800
This commit is contained in:
Sam Brannen
2024-10-28 11:42:56 +01:00
parent 52e813d0ad
commit 81d89f478a
8 changed files with 69 additions and 27 deletions

View File

@@ -237,16 +237,16 @@ class BeanOverrideBeanFactoryPostProcessorTests {
assertThat(context.getBean(beanName)).isEqualTo("overridden");
}
@Test
void replaceBeanByNameWithMatchingBeanDefinitionForClassBasedNonSingletonFactoryBeanFails() {
@Test // gh-33800
void replaceBeanByNameWithMatchingBeanDefinitionForClassBasedNonSingletonFactoryBean() {
String beanName = "descriptionBean";
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(NonSingletonStringFactoryBean.class);
context.registerBeanDefinition(beanName, factoryBeanDefinition);
assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("Unable to override bean 'descriptionBean': only singleton beans can be overridden.");
assertThatNoException().isThrownBy(context::refresh);
assertThat(context.isSingleton(beanName)).as("isSingleton").isTrue();
assertThat(context.getBean(beanName)).isEqualTo("overridden");
}
@Test
@@ -261,16 +261,16 @@ class BeanOverrideBeanFactoryPostProcessorTests {
assertThat(context.getBean(beanName, MessageService.class).getMessage()).isEqualTo("overridden");
}
@Test
void replaceBeanByNameWithMatchingBeanDefinitionForInterfaceBasedNonSingletonFactoryBeanFails() {
@Test // gh-33800
void replaceBeanByNameWithMatchingBeanDefinitionForInterfaceBasedNonSingletonFactoryBean() {
String beanName = "messageServiceBean";
AnnotationConfigApplicationContext context = createContext(MessageServiceTestCase.class);
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(NonSingletonMessageServiceFactoryBean.class);
context.registerBeanDefinition(beanName, factoryBeanDefinition);
assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("Unable to override bean 'messageServiceBean': only singleton beans can be overridden.");
assertThatNoException().isThrownBy(context::refresh);
assertThat(context.isSingleton(beanName)).as("isSingleton").isTrue();
assertThat(context.getBean(beanName, MessageService.class).getMessage()).isEqualTo("overridden");
}
@Test