исключить @Component из @ComponentScan

90

У меня есть компонент , который я хочу , чтобы исключить из @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
это не сработает, если у вас где-то есть другая аннотация сканирования компонентов, у которой нет такого же фильтра
Башар Али Лабади
@Bashar Ali Labadi, разве это не особенность этой конструкции? Если вы хотите исключить его из всех сканирований компонентов, вероятно, это вообще не должен быть компонент 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]
    }     
}

Условная логика может быть основана на контексте, потому что у вас есть доступ к фабрике компонентов. Например, когда компонент «Бар» не зарегистрирован как bean-компонент:

    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 для условных выражений, для меня это намного чище, чем использование фильтров. У меня никогда не было фильтров, которые работали бы так же стабильно, как условная загрузка beans
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 02
9

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

В конце концов я решил проблему, используя @SpringBootApplication , который, согласно документации Spring, выполняет те же функции, что и три приведенных выше, в одной аннотации.

Еще один совет: сначала попробуйте без уточнения сканирования пакетов (без фильтра basePackages).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
Дорони
источник
Я пробовал это, но показывает ошибку как «Следующие классы не могут быть исключены, потому что они не являются классами автоконфигурации». excludeFilters с @ComponentScan работает нормально.
AzarEJ
1

В случае исключения тестового компонента или тестовой конфигурации Spring Boot 1.4 представил новые тестовые аннотации @TestComponentи@TestConfiguration .

Любоскрнак
источник
0

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

Мигель Перейра
источник