Spring-boot: @ComponentScan in application class overrides default excludeFilters specified in @SpringBootApplication

Created on 6 Nov 2017  路  11Comments  路  Source: spring-projects/spring-boot

This enhancement idea is based on the findings after this question in Stack Overflow:

https://stackoverflow.com/questions/47054716/componentscan-in-application-class-breaks-webmvctest-and-springboottest

For me it was not obvious that when adding @ComponentScan in the application class it would override the defaults set by @SpringBootApplication (mainly the defaults set for excludeFilters).

I think there are multiple possibilities to make this clearer:

  1. When Spring Boot finds a @ComponentScan in the application class and it does not have the two default exclude filters (TypeExcludeFilter and AutoConfigurationExcludeFilter) it should throw an exception. An attribute could also be added to @SpringBootApplication to let the developer bypass this check if desired.

  2. Would it make sense to simply deny the use of @ComponentScan in the application class?

  3. Spring Boot could automatically consider the two default filters even if they are not specified in the @ComponentScan annotation when it is in the application class.

  4. Would it make sense to move the default exclude filters from the @SpringBootApplication annotation to the @ComponentScann annotation?

documentation

Most helpful comment

I am not sure about what may go wrong when AutoConfigurationExcludeFilter is missing.

Generally speaking, nothing. This filter just prevents accidentally component scanning on auto-configuration classes that should, instead, be found via entries in spring.factories.

the reason for adding ComponentScan was to effectively add a custom excludeFilter

Boot deliberately backs off when we see the user expressing their own opinion. In this case you've expressed an opinion about the exclude filters so the defaults no longer apply. If we always kept the defaults, there'd be no way to switch them off. The approach we've taken means that you can either take complete control and remove the defaults, or you can keep them by explicitly declaring them in your excludeFilter configuration.

I think we should update the documentation to make it clearer that using @ComponentScan will replace the default exclude filters and that, if you want to keep them, you should declare them explicitly.

All 11 comments

Your finding is not fully complete, actually. It's not about the exclude not being added, it's about you having an opinion as which packages should be scanned on the source of the context for the test. This section of the 2.0 documentation provides more details.

With that in mind, I don't think that we should change anything.

Well, in fact the documentation that you pointed also does not cover the complete picture as well when it says: "This effectively overrides the default component scan directive with the side effect of scanning those two packages regardless of the slice that you chose.". It gives the impression that scanning both packages is the only side effect.

In the case of Stack Overflow question, the package scan is the same for both cases and the reason for adding ComponentScan was to effectively add a custom excludeFilter. Without the default excludeFilters specified in the SpringBootApplication annotation the filter TestTypeExcludeFilter will not be used in the tests and this is what caused the problem and not the fact that it was scanning other things.

It gives the impression that scanning both packages is the only side effect.

In the context of that section, this is the only side effect. If you think there is a phrasing that would be more complete/accurate, I am happy to review a PR.

This is what caused the problem and not the fact that it was scanning other things.

It doesn't matter what packages you scan. If you put it in your @SpringBootApplication class you are providing a hard-coded opinion. If you can move it to a separate @Configuration class, then the doc covers exactly that use case.

If you can't, please share more details, perhaps we can improve that then.

@SpringBootApplication has an internal @CompoentScan on its declaration where there are two exclude filters (TypeExcludeFilter and AutoConfigurationExcludeFilter).

If you specify a @ComponentScan on the class annotated with @SpringBootApplication those two default exclude filters will be overwritten by whatever is in the new @ComponentScan annotation.

If TypeExcludeFilter is missing it will break the tests. I am not sure about what may go wrong when AutoConfigurationExcludeFilter is missing.

I am not sure about what may go wrong when AutoConfigurationExcludeFilter is missing.

Generally speaking, nothing. This filter just prevents accidentally component scanning on auto-configuration classes that should, instead, be found via entries in spring.factories.

the reason for adding ComponentScan was to effectively add a custom excludeFilter

Boot deliberately backs off when we see the user expressing their own opinion. In this case you've expressed an opinion about the exclude filters so the defaults no longer apply. If we always kept the defaults, there'd be no way to switch them off. The approach we've taken means that you can either take complete control and remove the defaults, or you can keep them by explicitly declaring them in your excludeFilter configuration.

I think we should update the documentation to make it clearer that using @ComponentScan will replace the default exclude filters and that, if you want to keep them, you should declare them explicitly.

On the other hand, if you don't want to touch the filters and think the @ComponentScan("x.y.z") is equivalent to @SpringBootApplication(scanBasePackages="x.y.z") then you felt on Spring Boot's evil trap.
They look equivalent, but they are not.
I think that if the developer does not specify a different excludeFilter in @ComponentScan it should get the default one for @SpringBootApplication instead of getting the default overridden by empty filters.

Initially in our project we did not have excludeFilters and we were already using @ComponentScan because we thought it was equivalent.

Another option would be:

  1. Add a warning message to the ComponentScan javadoc saying that if it is used in a class already annotated with SpringBootApplication it will override the default excludedFilters.

5 is not an option because @ComponentScan is part of Spring Framework and it doesn't know about any Spring Boot concepts.

Option #4 is invalid for the same reason then.

I don't think there's much we should do here other than add a note to the documentation.

Was this page helpful?
0 / 5 - 0 ratings