Allow @Bean method to override scanned class matching its return type

Closes gh-31052
This commit is contained in:
Juergen Hoeller 2023-08-15 13:55:57 +02:00
parent d89e305c87
commit 57f675c537
2 changed files with 46 additions and 11 deletions

View File

@ -301,8 +301,12 @@ class ConfigurationClassBeanDefinitionReader {
}
// A bean definition resulting from a component scan can be silently overridden
// by an @Bean method, as of 4.2...
if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
// by an @Bean method - and as of 6.1, even when general overriding is disabled
// as long as the bean class is the same.
if (existingBeanDef instanceof ScannedGenericBeanDefinition scannedBeanDef) {
if (beanMethod.getMetadata().getReturnTypeName().equals(scannedBeanDef.getBeanClassName())) {
this.registry.removeBeanDefinition(beanName);
}
return false;
}

View File

@ -16,7 +16,6 @@
package org.springframework.context.annotation;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -209,7 +208,7 @@ class ComponentScanAnnotationIntegrationTests {
}
@Test
void withScopedProxyThroughRegex() throws IOException, ClassNotFoundException {
void withScopedProxyThroughRegex() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ComponentScanWithScopedProxyThroughRegex.class);
ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
@ -221,7 +220,7 @@ class ComponentScanAnnotationIntegrationTests {
}
@Test
void withScopedProxyThroughAspectJPattern() throws IOException, ClassNotFoundException {
void withScopedProxyThroughAspectJPattern() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ComponentScanWithScopedProxyThroughAspectJPattern.class);
ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
@ -233,7 +232,7 @@ class ComponentScanAnnotationIntegrationTests {
}
@Test
void withMultipleAnnotationIncludeFilters1() throws IOException, ClassNotFoundException {
void withMultipleAnnotationIncludeFilters1() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(ComponentScanWithMultipleAnnotationIncludeFilters1.class);
ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated
@ -241,13 +240,30 @@ class ComponentScanAnnotationIntegrationTests {
}
@Test
void withMultipleAnnotationIncludeFilters2() throws IOException, ClassNotFoundException {
void withMultipleAnnotationIncludeFilters2() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(ComponentScanWithMultipleAnnotationIncludeFilters2.class);
ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated
ctx.getBean(MessageBean.class); // @CustomComponent-annotated
}
@Test
void withBeanMethodOverride() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters3.class);
ctx.refresh();
assertThat(ctx.getBean(DefaultNamedComponent.class).toString()).isEqualTo("overridden");
}
@Test
void withBeanMethodOverrideAndGeneralOverridingDisabled() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters3.class);
ctx.refresh();
assertThat(ctx.getBean(DefaultNamedComponent.class).toString()).isEqualTo("overridden");
}
@Test
void withBasePackagesAndValueAlias() {
AnnotationConfigApplicationContext ctx =
@ -292,6 +308,7 @@ class ComponentScanAnnotationIntegrationTests {
static class MultipleComposedAnnotationsConfig {
}
static class AwareTypeFilter implements TypeFilter, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware, BeanFactoryAware {
@ -329,10 +346,8 @@ class ComponentScanAnnotationIntegrationTests {
assertThat(this.environment).isNotNull();
return false;
}
}
}
@ -461,11 +476,27 @@ class ComponentScanWithMultipleAnnotationIncludeFilters1 {}
)
class ComponentScanWithMultipleAnnotationIncludeFilters2 {}
@Configuration
@ComponentScan(basePackages = "example.scannable",
useDefaultFilters = false,
includeFilters = @Filter({CustomStereotype.class, CustomComponent.class})
)
class ComponentScanWithMultipleAnnotationIncludeFilters3 {
@Bean
public DefaultNamedComponent thoreau() {
return new DefaultNamedComponent() {
@Override
public String toString() {
return "overridden";
}
};
}
}
@Configuration
@ComponentScan(
value = "example.scannable",
basePackages = "example.scannable",
basePackageClasses = example.scannable.PackageMarker.class)
class ComponentScanWithBasePackagesAndValueAlias {}