diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java index d7d9b6cd1d..1c8ff27eff 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java @@ -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 diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java index f7802d0fdd..8e2a1f1c20 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java @@ -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")