exclude @Component from @ComponentScan

194,491

Solution 1

The configuration seem alright, except that you should use excludeFilters instead of excludes:

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}

Solution 2

Using explicit types in scan filters is ugly for me. I believe more elegant approach is to create own marker annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreDuringScan {
}

Mark component that should be excluded with it:

@Component("foo") 
@IgnoreDuringScan
class Foo {
    ...
}

And exclude this annotation from your component scan:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}

Solution 3

Another approach is to use new conditional annotations. Since plain Spring 4 you can use @Conditional annotation:

@Component("foo")
@Conditional(FooCondition.class)
class Foo {
    ...
}

and define conditional logic for registering Foo component:

public class FooCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // return [your conditional logic]
    }     
}

Conditional logic can be based on context, because you have access to bean factory. For Example when "Bar" component is not registered as bean:

    return !context.getBeanFactory().containsBean(Bar.class.getSimpleName());

With Spring Boot (should be used for EVERY new Spring project), you can use these conditional annotations:

  • @ConditionalOnBean
  • @ConditionalOnClass
  • @ConditionalOnExpression
  • @ConditionalOnJava
  • @ConditionalOnMissingBean
  • @ConditionalOnMissingClass
  • @ConditionalOnNotWebApplication
  • @ConditionalOnProperty
  • @ConditionalOnResource
  • @ConditionalOnWebApplication

You can avoid Condition class creation this way. Refer to Spring Boot docs for more detail.

Solution 4

In case you need to define two or more excludeFilters criteria, you have to use the array.

For instances in this section of code I want to exclude all the classes in the org.xxx.yyy package and another specific class, MyClassToExclude

 @ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })

Solution 5

I had an issue when using @Configuration, @EnableAutoConfiguration and @ComponentScan while trying to exclude specific configuration classes, the thing is it didn't work!

Eventually I solved the problem by using @SpringBootApplication, which according to Spring documentation does the same functionality as the three above in one annotation.

Another Tip is to try first without refining your package scan (without the basePackages filter).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
Share:
194,491

Related videos on Youtube

user1002601
Author by

user1002601

Updated on October 26, 2021

Comments

  • user1002601
    user1002601 over 2 years

    I have a component that I want to exclude from a @ComponentScan in a particular @Configuration:

    @Component("foo") class Foo {
    ...
    }
    

    Otherwise, it seems to clash with some other class in my project. I don't fully understand the collision, but if I comment out the @Component annotation, things work like I want them to. But other projects that rely on this library expect this class to be managed by Spring, so I want to skip it only in my project.

    I tried using @ComponentScan.Filter:

    @Configuration 
    @EnableSpringConfigured
    @ComponentScan(basePackages = {"com.example"}, excludeFilters={
      @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
    public class MySpringConfiguration {}
    

    but it doesn't appear to work. If I try using FilterType.ASSIGNABLE_TYPE, I get a strange error about being unable to load some seemingly random class:

    Caused by: java.io.FileNotFoundException: class path resource [junit/framework/TestCase.class] cannot be opened because it does not exist

    I also tried using type=FilterType.CUSTOM as following:

    class ExcludeFooFilter implements TypeFilter {
        @Override
        public boolean match(MetadataReader metadataReader,
                MetadataReaderFactory metadataReaderFactory) throws IOException {
            return metadataReader.getClass() == Foo.class;
        }
    }
    
    @Configuration @EnableSpringConfigured
    @ComponentScan(basePackages = {"com.example"}, excludeFilters={
      @ComponentScan.Filter(type=FilterType.CUSTOM, value=ExcludeFooFilter.class)})
    public class MySpringConfiguration {}
    

    But that doesn't seem to exclude the component from the scan like I want.

    How do I exclude it?

  • user1002601
    user1002601 over 10 years
    sorry, that was a cut and paste error. I am using excludeFilters. I'll take another look, the error this gives me is really bizarre...
  • Kirby
    Kirby about 9 years
    That's a clever idea for universal exclusion though won't help if you want to exclude a component from only a subset of application contexts in a project. Really, to exclude it universally, one could just remove the @Component, but I don't think that's what the question is asking
  • wondergoat77
    wondergoat77 over 6 years
    +1 for the conditionals, this is a much cleaner way to me than using filters. I haven't ever gotten filters to work as consistently as conditional loading of beans
  • Bashar Ali Labadi
    Bashar Ali Labadi over 5 years
    this won't work if you have another component scan annotation somewhere that doesn't have the same filter
  • wutzebaer
    wutzebaer over 4 years
    i would suggest ASPECTJ as filter type, because this regex would also match "org.xxx.yyyy.z"
  • luboskrnac
    luboskrnac over 4 years
    @Bashar Ali Labadi, isn't that kind of point of this construct? If you want to exclude it from all component scans, it probably shouldn't be Spring component at all.
  • AzarEJ
    AzarEJ over 3 years
    I have tried this but showing error as "The following classes could not be excluded because they are not auto-configuration classes". excludeFilters with @ComponentScan is working fine.
  • Abhijit Sarkar
    Abhijit Sarkar almost 3 years
    This is wrong. Foo can only be an auto configuration class.
  • hello_earth
    hello_earth almost 3 years
    in my case, FilterType.ASSIGNABLE_TYPE did not work - I think it only works when the classes you want to exclude are also @Configuration classes. but FilterType.ASPECTJ worked - specifically it worked even e.g. for individual classes - doesn't have to refer to packages. Also if you have multiple @Configuration classes further down in your class hierarchy, the exclusion filters have to be applied to main @SpringBootApplication class (which is also a @Configuration) - otherwise it does component scan and manages to instantiate classes before other filters start to work.