виключити @Component із @ComponentScan


90

У мене є компонент, який я хочу виключити з a @ComponentScanу певному @Configuration:

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

В іншому випадку, здається, це зіткнеться з іншим класом у моєму проекті. Я не повністю розумію зіткнення, але якщо я коментую @Componentанотацію, все працює так, як я хочу. Але інші проекти, які покладаються на цю бібліотеку, очікують, що цим класом буде керувати Spring, тому я хочу пропустити його лише у своєму проекті.

Я спробував використовувати @ComponentScan.Filter:

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

але це, здається, не працює. Якщо я спробую використовувати FilterType.ASSIGNABLE_TYPE, я отримую дивну помилку про те, що не можу завантажити якийсь, здавалося б, випадковий клас:

Викликано: java.io.FileNotFoundException: ресурс шляху класу [junit / framework / TestCase.class] не може бути відкритий, оскільки він не існує

Я також спробував використовувати type=FilterType.CUSTOMнаступне:

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

Але це, здається, не виключає компонент із сканування, як я хочу.

Як це виключити?

Відповіді:


107

Конфігурація здається добре, за винятком того, що ви повинні використовувати excludeFiltersзамість excludes:

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

вибачте, це була помилка вирізання та вставки. Я використовую excludeFilters. Я ще раз подивлюсь, помилка, яку мені це видає, справді химерна ...
ykaganovich

52

Використання явних типів у фільтрах сканування для мене потворне. Я вважаю, що більш елегантним підходом є створення власної анотації маркера:

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

Позначте компонент, який слід виключити з ним:

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

І виключіть цю анотацію зі сканування компонентів:

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

13
Це розумна ідея універсального виключення, хоча не допоможе, якщо ви хочете виключити компонент лише з підмножини контекстів програм у проекті. Дійсно, щоб загально виключити його, можна просто видалити @Component, але я не думаю, що це питання
Кирбі,

2
це не спрацює, якщо у вас десь є анотація сканування компонентів, де немає такого самого фільтра
Башар Алі Лабаді,

@ Башар Алі Лабаді, чи не такий момент у цій конструкції? Якщо ви хочете виключити його зі всіх сканувань компонентів, це, мабуть, не повинно бути компонентом Spring.
luboskrnac

30

Інший підхід полягає у використанні нових умовних анотацій. З простої Spring 4 ви можете використовувати @Conditional анотацію:

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

та визначити умовну логіку для реєстрації компонента Foo:

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

Умовна логіка може базуватися на контексті, оскільки ви маєте доступ до фабрики зернових. Наприклад, коли компонент "Bar" не зареєстрований як компонент:

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

З Spring Boot (слід використовувати для КОЖНОГО нового проекту Spring) ви можете використовувати такі умовні анотації:

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

Ви можете уникнути створення класу Condition таким чином. Докладніше див. У документації Spring Boot.


1
+1 для умовних, це набагато чистіший спосіб для мене, ніж використання фільтрів. Я ніколи не отримував, щоб фільтри працювали так послідовно, як умовне завантаження квасолі
wondergoat77

13

Якщо вам потрібно визначити два або більше критерії excludeFilters , вам доведеться використовувати масив.

Для випадків у цьому розділі коду я хочу виключити всі класи в пакеті org.xxx.yyy та інший конкретний клас, MyClassToExclude

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

я б запропонував ASPECTJ як тип фільтра, оскільки цей регулярний вираз також відповідав би "org.xxx.yyyy.z"
wutzebaer,

9

У мене виникла проблема з використанням @Configuration , @EnableAutoConfiguration та @ComponentScan під час спроби виключити певні класи конфігурації, справа в тому, що це не спрацювало!

Врешті-решт я вирішив проблему за допомогою @SpringBootApplication , який згідно з документацією Spring виконує ту саму функціональність, що і три вищевказані в одній анотації.

Ще одна порада - спробувати спочатку без уточнення сканування пакета (без фільтра basePackages).

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

Я спробував це, але показав помилку як "Наступні класи не можна виключити, оскільки вони не є класами автоматичної конфігурації". excludeFilters з @ComponentScan працює нормально.
AzarEJ


0

Мені потрібно було виключити аудит @Aspect @Component із контексту програми, але лише для кількох тестових класів. У підсумку я використав @Profile ("аудит") у класі аспектів; включаючи профіль для звичайних операцій, але виключаючи його (не розміщуйте в @ActiveProfiles) для конкретних тестових класів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.