From 121c0ac2851528185329a978f732dd9b015ebc94 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 27 May 2026 16:31:13 +0200 Subject: [PATCH] Remove scanned class only when conflicting with imported class Closes gh-36835 --- .../annotation/ConfigurationClassParser.java | 8 +++++--- .../ComponentScanAnnotationIntegrationTests.java | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 782bbe8ab99..d2af1f31a48 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -260,9 +260,11 @@ class ConfigurationClassParser { return; } else if (configClass.isScanned()) { - String beanName = configClass.getBeanName(); - if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { - this.registry.removeBeanDefinition(beanName); + if (existingClass.isImported()) { + String beanName = configClass.getBeanName(); + if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { + this.registry.removeBeanDefinition(beanName); + } } // An implicitly scanned bean definition should not override an explicit import. return; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index 133b44d5716..e8f189fdcf4 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -26,6 +26,7 @@ import example.scannable.CustomComponent; import example.scannable.CustomStereotype; import example.scannable.DefaultNamedComponent; import example.scannable.FooService; +import example.scannable.FooServiceImpl; import example.scannable.MessageBean; import example.scannable.ScopedProxyTestBean; import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage; @@ -43,6 +44,7 @@ import org.springframework.beans.factory.annotation.CustomAutowireConfigurer; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; @@ -84,6 +86,17 @@ class ComponentScanAnnotationIntegrationTests { assertContextContainsBean(ctx, "fooServiceImpl"); } + @Test + void controlScanWithExplicitRegistration() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.registerBeanDefinition("myFooService", new RootBeanDefinition(FooServiceImpl.class)); + ctx.scan(example.scannable.PackageMarker.class.getPackage().getName()); + ctx.refresh(); + + assertContextContainsBean(ctx, "myFooService"); + assertContextContainsBean(ctx, "fooServiceImpl"); + } + @Test void viaContextRegistration() { ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig.class);