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ć?

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

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

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.


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

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

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


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.