exclude @Component from @ComponentScan

JavaSpringDependency InjectionSpring BootSpring Ioc

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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 - Java

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 - Java

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 - Java

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 - Java

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 {}

Solution 6 - Java

In case of excluding test component or test configuration, Spring Boot 1.4 introduced new testing annotations @TestComponent and @TestConfiguration.

Solution 7 - Java

I needed to exclude an auditing @Aspect @Component from the app context but only for a few test classes. I ended up using @Profile("audit") on the aspect class; including the profile for normal operations but excluding it (don't put it in @ActiveProfiles) on the specific test classes.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionykaganovichView Question on Stackoverflow
Solution 1 - JavaSergi AlmarView Answer on Stackoverflow
Solution 2 - JavaluboskrnacView Answer on Stackoverflow
Solution 3 - JavaluboskrnacView Answer on Stackoverflow
Solution 4 - JavaEnrico GiurinView Answer on Stackoverflow
Solution 5 - JavadoronyView Answer on Stackoverflow
Solution 6 - JavaluboskrnacView Answer on Stackoverflow
Solution 7 - JavaMiguel PereiraView Answer on Stackoverflow