@Component von @ComponentScan ausschließen

90

Ich habe eine Komponente, die ich @ComponentScanin einer bestimmten von einer ausschließen möchte @Configuration:

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

Ansonsten scheint es mit einer anderen Klasse in meinem Projekt zusammenzustoßen. Ich verstehe die Kollision nicht vollständig, aber wenn ich die @ComponentAnmerkung auskommentiere, funktionieren die Dinge so, wie ich es möchte. Andere Projekte, die auf dieser Bibliothek basieren, erwarten jedoch, dass diese Klasse von Spring verwaltet wird. Daher möchte ich sie nur in meinem Projekt überspringen.

Ich habe versucht @ComponentScan.Filter:

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

aber es scheint nicht zu funktionieren. Wenn ich es versuche FilterType.ASSIGNABLE_TYPE, erhalte ich einen seltsamen Fehler, weil ich eine scheinbar zufällige Klasse nicht laden kann:

Auslöser: java.io.FileNotFoundException: Die Klassenpfadressource [junit / framework / TestCase.class] kann nicht geöffnet werden, da sie nicht vorhanden ist

Ich habe auch versucht, type=FilterType.CUSTOMFolgendes zu verwenden:

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

Aber das scheint die Komponente nicht wie gewünscht vom Scan auszuschließen.

Wie schließe ich es aus?

ykaganovich
quelle
Kannst du die Antwort akzeptieren, wenn einer für dich gearbeitet hat?
Nikhil Sahu

Antworten:

104

Die Konfiguration scheint in Ordnung zu sein, außer dass Sie excludeFiltersanstelle von excludes:

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Sergi Almar
quelle
Entschuldigung, das war ein Fehler beim Ausschneiden und Einfügen. Ich benutze excludeFilters. Ich werde noch einen Blick darauf werfen, der Fehler, den ich dadurch
bekomme
51

Die Verwendung expliziter Typen in Scanfiltern ist für mich hässlich. Ich glaube, ein eleganterer Ansatz besteht darin, eine eigene Markierungsanmerkung zu erstellen:

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

Markieren Sie die Komponente, die damit ausgeschlossen werden soll:

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

Und schließen Sie diese Anmerkung von Ihrem Komponentenscan aus:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
luboskrnac
quelle
13
Dies ist eine clevere Idee für den universellen Ausschluss. Sie hilft jedoch nicht, wenn Sie eine Komponente nur aus einer Teilmenge von Anwendungskontexten in einem Projekt ausschließen möchten. Wirklich, um es allgemein auszuschließen, könnte man das einfach entfernen @Component, aber ich glaube nicht, dass das die Frage ist
Kirby
2
Dies funktioniert nicht, wenn Sie irgendwo eine andere Komponente-Scan-Annotation haben, die nicht den gleichen Filter hat
Bashar Ali Labadi
@ Bashar Ali Labadi, ist das nicht der Sinn dieses Konstrukts? Wenn Sie es von allen Komponentenscans ausschließen möchten, sollte es wahrscheinlich überhaupt keine Spring-Komponente sein.
Luboskrnac
29

Ein anderer Ansatz besteht darin, neue bedingte Anmerkungen zu verwenden. Seit dem einfachen Spring 4 können Sie @Conditional Annotation verwenden:

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

und definiere bedingte Logik zum Registrieren der Foo-Komponente:

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

Die bedingte Logik kann auf dem Kontext basieren, da Sie Zugriff auf die Bean Factory haben. Zum Beispiel, wenn die Komponente "Bar" nicht als Bean registriert ist:

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

Mit Spring Boot (sollte für JEDES neue Spring-Projekt verwendet werden) können Sie diese bedingten Anmerkungen verwenden:

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

Auf diese Weise können Sie die Erstellung von Bedingungsklassen vermeiden. Weitere Informationen finden Sie in den Spring Boot-Dokumenten.

luboskrnac
quelle
1
+1 für die Bedingungen ist dies für mich ein viel saubererer Weg als die Verwendung von Filtern. Ich habe noch nie Filter bekommen, die so konsistent funktionieren wie das bedingte Laden von Bohnen
Wondergoat77
12

Falls Sie zwei oder mehr excludeFilters definieren müssen Sie Kriterien , müssen Sie das Array verwenden.

Für Instanzen in diesem Codeabschnitt möchte ich alle Klassen im Paket org.xxx.yyy und eine andere bestimmte Klasse, MyClassToExclude , ausschließen

 @ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })
Enrico Giurin
quelle
Ich würde ASPECTJ als Filtertyp vorschlagen, da dieser reguläre
Ausdruck
9

Ich hatte ein Problem bei der Verwendung von @Configuration , @EnableAutoConfiguration und @ComponentScan beim Versuch, bestimmte Konfigurationsklassen auszuschließen. Die Sache ist, dass es nicht funktioniert hat!

Schließlich löste ich das Problem mit @SpringBootApplication , das laut Spring-Dokumentation dieselbe Funktionalität wie die drei oben genannten in einer Anmerkung bietet.

Ein weiterer Tipp ist, es zuerst zu versuchen, ohne den Paket-Scan zu verfeinern (ohne den basePackages-Filter).

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

Ich musste eine Prüfung @Aspect @Component aus dem App-Kontext ausschließen, aber nur für einige Testklassen. Am Ende habe ich @Profile ("audit") für die Aspektklasse verwendet. Einschließen des Profils für den normalen Betrieb, aber Ausschließen (nicht in @ActiveProfiles einfügen) für die spezifischen Testklassen.

Miguel Pereira
quelle