web-dev-qa-db-fra.com

exclure @Component de @ComponentScan

J'ai un composant que je veux exclure d'un @ComponentScan dans un @Configuration particulier:

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

Sinon, cela semble entrer en conflit avec une autre classe de mon projet. Je ne comprends pas bien la collision, mais si je commente l'annotation @Component, tout fonctionne comme je le souhaite. Mais d'autres projets qui s'appuient sur cette bibliothèque s'attendent à ce que cette classe soit gérée par Spring. Je souhaite donc l'ignorer 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, j'obtiens une étrange erreur sur l'impossibilité de charger une classe apparemment aléatoire:

Cause: Java.io.FileNotFoundException: la ressource de chemin de classe [junit/framework/TestCase.class] ne peut pas être ouverte car elle n'existe pas.

J'ai aussi 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.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}

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

Comment puis-je l'exclure?

57
ykaganovich

La configuration semble correcte, sauf que vous devez 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 {}
68
Sergi Almar

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

public @interface IgnoreDuringScan {
}

Marquez le composant à exclure:

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

Et excluez cette annotation de votre analyse de composant:

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

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

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

et définir une logique conditionnelle pour l’enregistrement du composant Foo:

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

La logique conditionnelle peut être basée sur le contexte, car vous avez accès à la fabrique 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 (à utiliser pour CHAQUE nouveau projet Spring), vous pouvez utiliser les annotations conditionnelles suivantes:

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

Vous pouvez éviter la création de classe de condition de cette façon. Reportez-vous à la documentation Spring Boot pour plus de détails.

20
luboskrnac

Si vous devez définir deux ou plusieurs excludeFilters critères, vous devez utiliser le tableau. 

Par exemple, dans cette section de code, je souhaite exclure toutes les classes du org.xxx.yyy package 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) })
8
Enrico Giurin

J'ai eu un problème lors de l'utilisation de @Configuration, @EnableAutoConfiguration et @ComponentScan lors d'une tentative d'exclusion de classes de configuration spécifiques, le problème est que cela n'a pas fonctionné! 

Finalement, j'ai résolu le problème en utilisant @SpringBootApplication , qui, selon la documentation de Spring, offre les mêmes fonctionnalités que les trois précédentes en une seule annotation.

Une autre astuce consiste à essayer d’abord sans affiner l’analyse de votre paquet (sans le filtre basePackages).

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

En cas d'exclusion de composant de test ou de configuration de test, Spring Boot 1.4 a introduit de nouvelles annotations de test @TestComponent ET @TestConfiguration .

1
luboskrnac

J'avais besoin d'exclure un audit @Aspect @Component du contexte de l'application, mais uniquement pour quelques classes de test. J'ai fini par utiliser @Profile ("audit") sur la classe d'aspect; incluant le profil pour les opérations normales mais l'excluant (ne le mettez pas dans @ActiveProfiles) sur les classes de test spécifiques.

0
Miguel Pereira