Reject bean names with factory prefix for Bean Overrides

Closes gh-33674
This commit is contained in:
Sam Brannen 2024-10-09 14:14:29 +02:00
parent c864afd6fe
commit b5c82b8dcb
2 changed files with 35 additions and 12 deletions

View File

@ -96,6 +96,13 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
}
private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata) {
String beanName = overrideMetadata.getBeanName();
Field field = overrideMetadata.getField();
Assert.state(!BeanFactoryUtils.isFactoryDereference(beanName),() -> """
Unable to override bean '%s' for field '%s.%s': a FactoryBean cannot be overridden. \
To override the bean created by the FactoryBean, remove the '&' prefix.""".formatted(
beanName, field.getDeclaringClass().getSimpleName(), field.getName()));
switch (overrideMetadata.getStrategy()) {
case REPLACE_DEFINITION -> replaceDefinition(beanFactory, overrideMetadata, true);
case REPLACE_OR_CREATE_DEFINITION -> replaceDefinition(beanFactory, overrideMetadata, false);
@ -123,18 +130,16 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
RootBeanDefinition pseudoBeanDefinition = createPseudoBeanDefinition(overrideMetadata);
String beanName = overrideMetadata.getBeanName();
String beanNameIncludingFactory;
BeanDefinition existingBeanDefinition = null;
if (beanName == null) {
beanNameIncludingFactory = getBeanNameForType(beanFactory, overrideMetadata, requireExistingDefinition);
if (beanNameIncludingFactory == null) {
beanName = getBeanNameForType(beanFactory, overrideMetadata, requireExistingDefinition);
if (beanName == null) {
// We need to generate a name for a nonexistent bean.
beanName = beanNameGenerator.generateBeanName(pseudoBeanDefinition, registry);
beanNameIncludingFactory = beanName;
}
else {
// We are overriding an existing bean.
beanName = BeanFactoryUtils.transformedBeanName(beanNameIncludingFactory);
beanName = BeanFactoryUtils.transformedBeanName(beanName);
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
}
}
@ -149,7 +154,6 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
with name [%s] and type [%s]."""
.formatted(beanName, overrideMetadata.getBeanType()));
}
beanNameIncludingFactory = beanName;
}
if (existingBeanDefinition != null) {
@ -177,7 +181,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
Object override = overrideMetadata.createOverride(beanName, existingBeanDefinition, null);
overrideMetadata.track(override, beanFactory);
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanNameIncludingFactory);
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanName);
// Now we have an instance (the override) that we can register. At this stage, we don't
// expect a singleton instance to be present. If for some reason a singleton instance

View File

@ -56,6 +56,18 @@ import static org.mockito.Mockito.mock;
*/
class BeanOverrideBeanFactoryPostProcessorTests {
@Test
void beanNameWithFactoryBeanPrefixIsRejected() {
AnnotationConfigApplicationContext context = createContext(FactoryBeanPrefixTestCase.class);
assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("""
Unable to override bean '&messageService' for field 'FactoryBeanPrefixTestCase.messageService': \
a FactoryBean cannot be overridden. To override the bean created by the FactoryBean, remove the \
'&' prefix.""");
}
@Test
void replaceBeanByNameWithMatchingBeanDefinition() {
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
@ -348,6 +360,18 @@ class BeanOverrideBeanFactoryPostProcessorTests {
}
@FunctionalInterface
interface MessageService {
String getMessage();
}
static class FactoryBeanPrefixTestCase {
@DummyBean(beanName = "&messageService")
MessageService messageService;
}
static class CaseByName {
@DummyBean(beanName = "descriptionBean")
@ -464,11 +488,6 @@ class BeanOverrideBeanFactoryPostProcessorTests {
}
}
@FunctionalInterface
interface MessageService {
String getMessage();
}
static class MessageServiceTestCase {
@TestBean(name = "messageServiceBean")