116 votes

Exclure @Component de @ComponentScan

J'ai un composant que je veux exclure d'un @ComponentScan dans une @Configuration particulière :

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

Autrement, il semble entrer en conflit avec une autre classe de mon projet. Je ne comprends pas entièrement la collision, mais si je commente l'annotation @Component, les choses fonctionnent comme je le souhaite. Cependant, d'autres projets qui dépendent de cette bibliothèque s'attendent à ce que cette classe soit gérée par Spring, donc je veux la sauter uniquement dans mon projet.

J'ai essayé d'utiliser @ComponentScan.Filter:

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

mais cela ne semble pas fonctionner. Si j'essaie d'utiliser FilterType.ASSIGNABLE_TYPE, je reçois une erreur étrange disant qu'il est impossible de charger une classe apparemment aléatoire :

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

J'ai également essayé d'utiliser type=FilterType.CUSTOM comme suit :

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

Mais cela ne semble pas exclure le composant de l'analyse comme je le souhaite.

Comment puis-je l'exclure ?

141voto

Sergi Almar Points 4926

La configuration semble correcte, sauf que vous devriez utiliser excludeFilters au lieu de excludes:

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

63voto

luboskrnac Points 5279

Utiliser des types explicites dans les filtres de scan est laid pour moi. Je crois qu'une approche plus élégante est de créer sa propre annotation de marqueur :

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

Marquer le composant qui doit être exclu avec :

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

Et exclure cette annotation de votre scan de composants :

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

36voto

luboskrnac Points 5279

Une autre approche consiste à utiliser de nouvelles annotations conditionnelles. Depuis Spring 4 plain, vous pouvez utiliser l'annotation @Conditional :

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

et définir une logique conditionnelle pour enregistrer le composant Foo :

public class FooCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // return [votre logique conditionnelle]
    }     
}

La logique conditionnelle peut être basée sur le contexte, car vous avez accès à l'usine de beans. Par exemple lorsque le composant "Bar" n'est pas enregistré en tant que bean :

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

Avec Spring Boot (devrait être utilisé pour CHAQUE nouveau projet Spring), vous pouvez utiliser ces annotations conditionnelles :

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

Vous pouvez éviter la création de la classe Condition de cette manière. Consultez la documentation de Spring Boot pour plus de détails.

23voto

Enrico Giurin Points 66

Si vous avez besoin de définir deux critères ou plus de excludeFilters, vous devez utiliser le tableau.

Par exemple, dans cette section de code, je veux exclure toutes les classes du package org.xxx.yyy et une autre classe spécifique, MyClassToExclude

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

6voto

dorony Points 363

J'ai eu un problème en utilisant @Configuration, @EnableAutoConfiguration et @ComponentScan en essayant d'exclure des classes de configuration spécifiques, le souci était que cela ne fonctionnait pas!

Finalement, j'ai résolu le problème en utilisant @SpringBootApplication, qui, selon la documentation de Spring, fait la même fonction que les trois ci-dessus en une seule annotation.

Un autre conseil est d'essayer d'abord sans affiner votre balayage de package (sans le filtre basePackages).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X