escludere @Component da @ComponentScan


90

Ho un componente che voglio escludere da un @ComponentScanin particolare @Configuration:

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

Altrimenti, sembra scontrarsi con qualche altra classe nel mio progetto. Non capisco appieno la collisione, ma se commento l' @Componentannotazione, le cose funzionano come voglio. Ma altri progetti che si basano su questa libreria si aspettano che questa classe venga gestita da Spring, quindi voglio saltarla solo nel mio progetto.

Ho provato a usare @ComponentScan.Filter:

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

ma non sembra funzionare. Se provo a utilizzare FilterType.ASSIGNABLE_TYPE, ottengo uno strano errore di non essere in grado di caricare una classe apparentemente casuale:

Causato da: java.io.FileNotFoundException: la risorsa del percorso di classe [junit / framework / TestCase.class] non può essere aperta perché non esiste

Ho anche provato a usare type=FilterType.CUSTOMcome segue:

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

Ma ciò non sembra escludere il componente dalla scansione come desidero.

Come lo escludo?

Risposte:


108

La configurazione sembra a posto, tranne per il fatto che dovresti usare al excludeFiltersposto di excludes:

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

scusa, è stato un errore di copia e incolla. Sto usando excludeFilters. Darò un'altra occhiata, l'errore che questo mi dà è davvero bizzarro ...
ykaganovich

52

L'uso di tipi espliciti nei filtri di scansione è brutto per me. Credo che un approccio più elegante sia creare la propria annotazione del marker:

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

Contrassegna il componente che dovrebbe essere escluso con esso:

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

Ed escludi questa annotazione dalla scansione del tuo componente:

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

13
Questa è un'idea intelligente per l'esclusione universale anche se non aiuta se si desidera escludere un componente solo da un sottoinsieme di contesti applicativi in ​​un progetto. Davvero, per escluderlo universalmente, si potrebbe semplicemente rimuovere il @Component, ma non credo sia quello che si pone la domanda
Kirby

2
questo non funzionerà se hai un'annotazione di scansione di un altro componente da qualche parte che non ha lo stesso filtro
Bashar Ali Labadi

@Bashar Ali Labadi, non è questo il punto di questo costrutto? Se vuoi escluderlo da tutte le scansioni dei componenti, probabilmente non dovrebbe essere affatto un componente Spring.
Luboskrnac

30

Un altro approccio consiste nell'usare nuove annotazioni condizionali. Dal semplice Spring 4 puoi usare l'annotazione @Conditional:

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

e definire la logica condizionale per la registrazione del componente Foo:

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

La logica condizionale può essere basata sul contesto, perché hai accesso alla fabbrica di fagioli. Ad esempio, quando il componente "Bar" non è registrato come bean:

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

Con Spring Boot (dovrebbe essere usato per OGNI nuovo progetto Spring), puoi usare queste annotazioni condizionali:

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

Puoi evitare la creazione della classe Condizione in questo modo. Fare riferimento alla documentazione di Spring Boot per maggiori dettagli.


1
+1 per i condizionali, per me questo è un modo molto più pulito rispetto all'uso dei filtri. Non ho mai fatto funzionare i filtri in modo coerente come il caricamento condizionale dei fagioli
wondergoat77

13

Nel caso in cui sia necessario definire due o più criteri excludeFilters , è necessario utilizzare l'array.

Per le istanze in questa sezione di codice desidero escludere tutte le classi nel pacchetto org.xxx.yyy e un'altra classe specifica, MyClassToExclude

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

Suggerirei ASPECTJ come tipo di filtro, perché questa espressione regolare corrisponderebbe anche a "org.xxx.yyyy.z"
wutzebaer

9

Ho avuto un problema durante l'utilizzo di @Configuration , @EnableAutoConfiguration e @ComponentScan durante il tentativo di escludere classi di configurazione specifiche, il fatto è che non ha funzionato!

Alla fine ho risolto il problema utilizzando @SpringBootApplication , che secondo la documentazione di Spring ha le stesse funzionalità delle tre precedenti in un'unica annotazione.

Un altro suggerimento è provare prima senza perfezionare la scansione del pacchetto (senza il filtro basePackages).

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

1
Ho provato questo ma ho mostrato l'errore come "Le seguenti classi non possono essere escluse perché non sono classi di configurazione automatica". excludeFilters con @ComponentScan funziona correttamente.
AzarEJ


0

Avevo bisogno di escludere un controllo @Aspect @Component dal contesto dell'app, ma solo per alcune classi di test. Ho finito per usare @Profile ("audit") sulla classe di aspetto; includendo il profilo per le normali operazioni ma escludendolo (non metterlo in @ActiveProfiles) sulle classi di test specifiche.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.