wyklucz @Component z @ComponentScan

90

Mam składnik, który chcę @ComponentScanw szczególności wykluczyć @Configuration:

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

W przeciwnym razie wydaje się kolidować z inną klasą w moim projekcie. Nie do końca rozumiem kolizję, ale jeśli skomentuję @Componentadnotację, wszystko działa tak, jak chcę. Ale inne projekty, które opierają się na tej bibliotece, oczekują, że ta klasa będzie zarządzana przez Spring, więc chcę ją pominąć tylko w moim projekcie.

Próbowałem użyć @ComponentScan.Filter:

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

ale wygląda na to, że nie działa. Jeśli spróbuję użyć FilterType.ASSIGNABLE_TYPE, pojawia się dziwny błąd dotyczący niemożności załadowania pozornie przypadkowej klasy:

Przyczyna: java.io.FileNotFoundException: nie można otworzyć zasobu ścieżki klasy [junit / framework / TestCase.class], ponieważ nie istnieje

Próbowałem również użyć type=FilterType.CUSTOMnastępujących:

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

Ale to nie wydaje się wykluczać komponentu ze skanowania, tak jak chcę.

Jak to wykluczyć?

ykaganovich
źródło

Odpowiedzi:

107

Konfiguracja wydaje się w porządku, z tym że excludeFilterszamiast excludes:

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Sergi Almar
źródło
przepraszam, to był błąd wycinania i wklejania. Używam excludeFilters. Przyjrzę się jeszcze raz, błąd, który mi daje, jest naprawdę dziwny ...
ykaganovich
52

Używanie jawnych typów w filtrach skanowania jest dla mnie brzydkie. Uważam, że bardziej eleganckim podejściem jest utworzenie własnej adnotacji znacznika:

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

Zaznacz komponent, który powinien być z nim wykluczony:

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

I wyklucz tę adnotację ze skanowania komponentu:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
luboskrnac
źródło
13
To sprytny pomysł na uniwersalne wykluczenie, ale nie pomoże, jeśli chcesz wykluczyć komponent tylko z podzbioru kontekstów aplikacji w projekcie. Naprawdę, aby wykluczyć to uniwersalnie, można po prostu usunąć @Component, ale nie sądzę, że o to chodzi
Kirby
2
to nie zadziała, jeśli masz gdzieś inną adnotację skanowania komponentu, która nie ma tego samego filtra
Bashar Ali Labadi,
@Bashar Ali Labadi, czy nie jest to taki punkt w tej konstrukcji? Jeśli chcesz wykluczyć go ze wszystkich skanów komponentów, prawdopodobnie nie powinien to być w ogóle komponent Spring.
luboskrnac
30

Innym podejściem jest użycie nowych adnotacji warunkowych. Od zwykłej Spring 4 możesz używać adnotacji @Conditional:

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

i zdefiniuj warunkową logikę rejestracji komponentu Foo:

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

Logika warunkowa może być oparta na kontekście, ponieważ masz dostęp do fabryki fasoli. Na przykład, gdy komponent „Bar” nie jest zarejestrowany jako ziarno:

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

Dzięki Spring Boot (powinien być używany dla KAŻDEGO nowego projektu Spring) możesz użyć następujących adnotacji warunkowych:

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

W ten sposób możesz uniknąć tworzenia klasy Warunków. Więcej szczegółów można znaleźć w dokumentacji Spring Boot.

luboskrnac
źródło
1
+1 dla warunków, jest to dla mnie znacznie czystszy sposób niż używanie filtrów. Nigdy nie udało mi się uzyskać filtrów, które działają tak konsekwentnie, jak warunkowe ładowanie ziaren
Wondergoat77
13

Jeśli chcesz zdefiniować dwa lub więcej kryteriów excludeFilters , musisz użyć tablicy.

W przypadku instancji w tej sekcji kodu chcę wykluczyć wszystkie klasy z pakietu org.xxx.yyy i inną określoną klasę, MyClassToExclude

 @ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })
Enrico Giurin
źródło
sugerowałbym ASPECTJ jako typ filtra, ponieważ to wyrażenie regularne również pasowałoby do „org.xxx.yyyy.z”
wutzebaer
9

Miałem problem podczas używania @Configuration , @EnableAutoConfiguration i @ComponentScan podczas próby wykluczenia określonych klas konfiguracyjnych, chodzi o to, że nie działało!

Ostatecznie rozwiązałem problem za pomocą @SpringBootApplication , która zgodnie z dokumentacją Springa ma taką samą funkcjonalność jak trzy powyższe w jednej adnotacji.

Inną wskazówką jest, aby spróbować najpierw bez udoskonalania skanowania pakietów (bez filtra basePackages).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
dorony
źródło
Próbowałem tego, ale wyświetlał błąd jako „Nie można wykluczyć następujących klas, ponieważ nie są one klasami automatycznej konfiguracji”. excludeFilters z @ComponentScan działa poprawnie.
AzarEJ
1

W przypadku wykluczenia komponentu testowego lub konfiguracji testowej, Spring Boot 1.4 wprowadził nowe adnotacje testowe @TestComponenti@TestConfiguration .

luboskrnac
źródło
0

Musiałem wykluczyć inspekcję @Aspect @Component z kontekstu aplikacji, ale tylko dla kilku klas testowych. Skończyło się na użyciu @Profile („audit”) w klasie aspektu; w tym profil dla normalnych operacji, ale wykluczając go (nie umieszczaj go w @ActiveProfiles) w określonych klasach testowych.

Miguel Pereira
źródło