Using Spring Boot 3.1.5, my main Application
class was annotated with both @SpringBootApplication
and @ComponentScan
. The latter is necessary as we have a multi-module project that needs to exclude certain classes to avoid conflicts.
I am not able to paste our exact class names, so this is an example:
@SpringBootApplication
@ComponentScan(basePackages = "my.company.module",
excludeFilters = {
@Filter(type=REGEX, pattern="my\\.company\\.module\\.foo\\..*"),
@Filter(type=REGEX, pattern="my\\.company\\.module\\.bar\\..*"),
@Filter(type=REGEX, pattern="my\\.company\\.module\\.baz\\..*")
},
nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class Application {
This worked fine for scanning all our components and autowiring everything together. After upgrading to Spring Boot 3.2, the component scan seems to be running twice: once on "my.company.module"
as expected with the appropriate filters, but then again on a blank package (thus re-scanning all our components and attempting to create them again, causing bean conflicts).
Using a debugger, I traced the change down into ConfigurationClassParser
in spring-context
, specifically to this line:
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class);
This specifically occurs when it is processing the main application class:
In 3.1.5, this set after the function call has 1 entry with an expected base package "my.company.module"
member.
In 3.2, this set has 2 entries after the function call, the first with "my.company.module"
base package member, and the second with a base package of an empty array.
After doing some more testing, it seems as though starting in 3.2, each @ComponentScan
is processed independently even if a package has already been scanned, causing Spring to believe that a component or bean might be duplicated when it isn't.
Ex: If my root package is "my.company"
and I do a component scan of "my.company"
and in another configuration have a scan of "my.company.foo"
, a component that appears in my.company.foo
package will be detected as a duplicate bean of itself.
Comment From: HzjNeverStop
I encountered the same issue where this incompatible change prevents me from controlling the scanning scope of the @SpringBootApplication
annotation using excludeFilters
.
I hope that the Spring Boot team can fix this issue in version 3.2.1.
Comment From: quaff
Please share a minimal reproducer.
Comment From: HzjNeverStop
Please share a minimal reproducer.
You can use this project to reproduce this issue: https://github.com/HzjNeverStop/ComponentScanReproducer
The main branch use SpringBoot 3.1.5,ChildBean will not register. Then change the SpringBoot version to 3.2.0,ChildBean will be scan and applicaiton init fail
Comment From: wilkinsona
Thanks for the sample. This appears to be a Spring Framework regression. The failure occurs with Spring Boot 3.1.5 (or 3.1.6) when Spring Framework is upgraded to 6.1. Trying various Framework versions, the problem appears to have been introduced in 6.1.0-M4. We'll transfer this to the Framework team so that they can investigate.
Comment From: HzjNeverStop
Thanks for the sample. This appears to be a Spring Framework regression. The failure occurs with Spring Boot 3.1.5 (or 3.1.6) when Spring Framework is upgraded to 6.1. Trying various Framework versions, the problem appears to have been introduced in 6.1.0-M4. We'll transfer this to the Framework team so that they can investigate.
Thanks,will SpringBoot 3.2.1 upgrade SpringFramework to the fixed version of this issue?
Comment From: snicoll
@HzjNeverStop please refrain from asking questions like that. The issue is opened and you can follow its state right here.
Comment From: snicoll
This seems to be a regression introduced by https://github.com/spring-projects/spring-framework/issues/30941
Comment From: sbrannen
I plan to address this by allowing a directly present @ComponentScan
annotation to always take precedence over any meta-present @ComponentScan
annotations (such as the one on @SpringBootApplication
).
Doing that would still allow us to support the "multiple meta-annotations" use case that we originally sought to support in 6.1 (see #30941).
In addition, the behavior for @ComponentScan
would then effectively align with the behavior for @BootstrapWith
in spring-test
in the sense that a locally declared annotation always overrides a meta-annotation.