Favor local @⁠ComponentScan annotations over meta-annotations

Work performed in conjunction with gh-30941 resulted in a regression.
Specifically, prior to Spring Framework 6.1 a locally declared
@⁠ComponentScan annotation took precedence over @⁠ComponentScan
meta-annotations, which allowed "local" configuration to override
"meta-present" configuration.

This commit modifies the @⁠ComponentScan search algorithm so that
locally declared @⁠ComponentScan annotations are once again favored
over @⁠ComponentScan meta-annotations (and, indirectly, composed
annotations).

See gh-30941 Closes gh-31704
This commit is contained in:
Sam Brannen
2023-12-06 11:36:38 +01:00
parent afcd03bddc
commit 6b53f37030
4 changed files with 149 additions and 4 deletions

View File

@@ -137,6 +137,50 @@ class ComponentScanAnnotationIntegrationTests {
assertContextContainsBean(ctx, "barComponent");
}
@Test
void localAnnotationOverridesMultipleMetaAnnotations() { // gh-31704
ApplicationContext ctx = new AnnotationConfigApplicationContext(LocalAnnotationOverridesMultipleMetaAnnotationsConfig.class);
assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalAnnotationOverridesMultipleMetaAnnotationsConfig");
assertContextContainsBean(ctx, "barComponent");
assertContextDoesNotContainBean(ctx, "simpleComponent");
assertContextDoesNotContainBean(ctx, "configurableComponent");
}
@Test
void localAnnotationOverridesMultipleComposedAnnotations() { // gh-31704
ApplicationContext ctx = new AnnotationConfigApplicationContext(LocalAnnotationOverridesMultipleComposedAnnotationsConfig.class);
assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalAnnotationOverridesMultipleComposedAnnotationsConfig");
assertContextContainsBean(ctx, "barComponent");
assertContextDoesNotContainBean(ctx, "simpleComponent");
assertContextDoesNotContainBean(ctx, "configurableComponent");
}
@Test
void localRepeatedAnnotationsOverrideComposedAnnotations() { // gh-31704
ApplicationContext ctx = new AnnotationConfigApplicationContext(LocalRepeatedAnnotationsOverrideComposedAnnotationsConfig.class);
assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalRepeatedAnnotationsOverrideComposedAnnotationsConfig");
assertContextContainsBean(ctx, "barComponent");
assertContextContainsBean(ctx, "configurableComponent");
assertContextDoesNotContainBean(ctx, "simpleComponent");
}
@Test
void localRepeatedAnnotationsInContainerOverrideComposedAnnotations() { // gh-31704
ApplicationContext ctx = new AnnotationConfigApplicationContext(LocalRepeatedAnnotationsInContainerOverrideComposedAnnotationsConfig.class);
assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalRepeatedAnnotationsInContainerOverrideComposedAnnotationsConfig");
assertContextContainsBean(ctx, "barComponent");
assertContextContainsBean(ctx, "configurableComponent");
assertContextDoesNotContainBean(ctx, "simpleComponent");
}
@Test
void viaBeanRegistration() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -299,6 +343,20 @@ class ComponentScanAnnotationIntegrationTests {
String[] basePackages() default {};
}
@Configuration
@ComponentScan("org.springframework.context.annotation.componentscan.simple")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MetaConfiguration1 {
}
@Configuration
@ComponentScan("example.scannable_implicitbasepackage")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MetaConfiguration2 {
}
@ComposedConfiguration(basePackages = "org.springframework.context.annotation.componentscan.simple")
static class ComposedAnnotationConfig {
}
@@ -308,6 +366,32 @@ class ComponentScanAnnotationIntegrationTests {
static class MultipleComposedAnnotationsConfig {
}
@MetaConfiguration1
@MetaConfiguration2
@ComponentScan("example.scannable.sub")
static class LocalAnnotationOverridesMultipleMetaAnnotationsConfig {
}
@ComposedConfiguration(basePackages = "org.springframework.context.annotation.componentscan.simple")
@ComposedConfiguration2(basePackages = "example.scannable_implicitbasepackage")
@ComponentScan("example.scannable.sub")
static class LocalAnnotationOverridesMultipleComposedAnnotationsConfig {
}
@ComposedConfiguration(basePackages = "org.springframework.context.annotation.componentscan.simple")
@ComponentScan("example.scannable_implicitbasepackage")
@ComponentScan("example.scannable.sub")
static class LocalRepeatedAnnotationsOverrideComposedAnnotationsConfig {
}
@ComposedConfiguration(basePackages = "org.springframework.context.annotation.componentscan.simple")
@ComponentScans({
@ComponentScan("example.scannable_implicitbasepackage"),
@ComponentScan("example.scannable.sub")
})
static class LocalRepeatedAnnotationsInContainerOverrideComposedAnnotationsConfig {
}
static class AwareTypeFilter implements TypeFilter, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware, BeanFactoryAware {