exclure @Component de @ComponentScan

90

J'ai un composant que je souhaite exclure d'un @ComponentScandans un particulier @Configuration:

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

Sinon, 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' @Componentannotation, les choses fonctionnent comme je le souhaite. Mais d'autres projets qui reposent sur cette bibliothèque s'attendent à ce que cette classe soit gérée par Spring, donc je veux 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'essaye d'utiliser FilterType.ASSIGNABLE_TYPE, j'obtiens une erreur étrange concernant l'impossibilité de charger une classe apparemment aléatoire:

Causée par: 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 également essayé d'utiliser type=FilterType.CUSTOMcomme 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 l'exclure?

Ykaganovich
la source

Réponses:

107

La configuration semble correcte, sauf que vous devriez utiliser à la excludeFiltersplace de excludes:

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Sergi Almar
la source
désolé, c'était une erreur de copier-coller. J'utilise excludeFilters. Je vais jeter un autre regard, l'erreur que cela me donne est vraiment bizarre ...
ykaganovich
52

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

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

Marquer le composant à exclure avec lui:

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

Et excluez cette annotation de l'analyse de vos composants:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
Luboskrnac
la source
13
C'est une idée intelligente d'exclusion universelle, mais ne vous aidera pas si vous souhaitez exclure un composant d'un sous-ensemble de contextes d'application dans un projet. Vraiment, pour l'exclure universellement, on pourrait simplement supprimer le @Component, mais je ne pense pas que ce soit ce que la question pose
Kirby
2
cela ne fonctionnera pas si vous avez une autre annotation d'analyse de composant quelque part qui n'a pas le même filtre
Bashar Ali Labadi
@Bashar Ali Labadi, n'est-ce pas ce genre de point de cette construction? Si vous souhaitez l'exclure de toutes les analyses de composants, il ne devrait probablement pas s'agir d'un composant Spring.
luboskrnac
30

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

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

et définissez 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 bean. Par exemple, lorsque le composant "Bar" n'est pas enregistré comme 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 classe Condition de cette façon. Reportez-vous à la documentation de Spring Boot pour plus de détails.

Luboskrnac
la source
1
+1 pour les conditions, c'est une façon beaucoup plus propre que d'utiliser des filtres. Je n'ai jamais fait fonctionner les filtres aussi régulièrement que le chargement conditionnel de haricots
wonderergoat77
13

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

Pour les instances de cette section de code, je souhaite 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) })
Enrico Giurin
la source
Je suggérerais ASPECTJ comme type de filtre, car cette expression régulière correspondrait également à "org.xxx.yyyy.z"
wutzebaer
9

J'ai eu un problème lors de l'utilisation de @Configuration , @EnableAutoConfiguration et @ComponentScan en essayant d'exclure des 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 ci-dessus dans une annotation.

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

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
Dorony
la source
J'ai essayé ceci mais affichant l'erreur comme "Les classes suivantes n'ont pas pu être exclues parce qu'elles ne sont pas des classes de configuration automatique". excludeFilters avec @ComponentScan fonctionne correctement.
AzarEJ le
1

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

Luboskrnac
la source
0

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; en incluant le profil pour les opérations normales mais en l'excluant (ne le mettez pas dans @ActiveProfiles) sur les classes de test spécifiques.

Miguel Pereira
la source