excluir @Component de @ComponentScan

90

Tengo un componente que quiero excluir de un @ComponentScanen un particular @Configuration:

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

De lo contrario, parece chocar con alguna otra clase en mi proyecto. No entiendo completamente la colisión, pero si comento la @Componentanotación, las cosas funcionan como quiero. Pero otros proyectos que dependen de esta biblioteca esperan que Spring administre esta clase, por lo que quiero omitirla solo en mi proyecto.

Intenté usar @ComponentScan.Filter:

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

pero no parece funcionar. Si intento usar FilterType.ASSIGNABLE_TYPE, obtengo un error extraño acerca de que no puedo cargar una clase aparentemente aleatoria:

Causado por: java.io.FileNotFoundException: recurso de ruta de clase [junit / framework / TestCase.class] no se puede abrir porque no existe

También intenté usar type=FilterType.CUSTOMlo siguiente:

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

Pero eso no parece excluir el componente del escaneo como quiero.

¿Cómo lo excluyo?

ykaganovich
fuente

Respuestas:

107

La configuración parece estar bien, excepto que debes usar en excludeFilterslugar de excludes:

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Sergi Almar
fuente
lo siento, fue un error de cortar y pegar. Estoy usando excludeFilters. Voy a tomar otra mirada, el error esto da de mí es realmente extraño ...
ykaganovich
52

Usar tipos explícitos en los filtros de escaneo es feo para mí. Creo que un enfoque más elegante es crear su propia anotación de marcador:

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

Marque el componente que debe excluirse con él:

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

Y excluya esta anotación de su análisis de componentes:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
luboskrnac
fuente
13
Esa es una idea inteligente para la exclusión universal, aunque no ayudará si desea excluir un componente de solo un subconjunto de contextos de aplicación en un proyecto. Realmente, para excluirlo universalmente, uno podría simplemente eliminar el @Component, pero no creo que eso sea lo que pregunta la pregunta
Kirby
2
esto no funcionará si tiene otra anotación de escaneo de componentes en algún lugar que no tenga el mismo filtro
Bashar Ali Labadi
@Bashar Ali Labadi, ¿no es ese el punto de esta construcción? Si desea excluirlo de todos los análisis de componentes, probablemente no debería ser un componente Spring en absoluto.
luboskrnac
30

Otro enfoque consiste en utilizar nuevas anotaciones condicionales. Desde simple Spring 4, puede usar la anotación @Conditional:

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

y definir la lógica condicional para registrar el componente Foo:

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

La lógica condicional puede basarse en el contexto, porque tiene acceso a la fábrica de frijoles. Por ejemplo, cuando el componente "Bar" no está registrado como bean:

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

Con Spring Boot (debe usarse para CADA nuevo proyecto de Spring), puede usar estas anotaciones condicionales:

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

Puede evitar la creación de clases de condición de esta manera. Consulte los documentos de Spring Boot para obtener más detalles.

luboskrnac
fuente
1
+1 para los condicionales, esta es una forma mucho más limpia para mí que usar filtros. Nunca he conseguido que los filtros funcionen de manera tan consistente como la carga condicional de beans
wondergoat77
13

En caso de que necesite definir dos o más criterios excludeFilters , debe usar la matriz.

Para los casos en esta sección de código, quiero excluir todas las clases en el paquete org.xxx.yyy y otra clase específica, MyClassToExclude

 @ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })
Enrico Giurin
fuente
Sugeriría ASPECTJ como tipo de filtro, porque esta expresión regular también coincidiría con "org.xxx.yyyy.z"
wutzebaer
9

Tuve un problema al usar @Configuration , @EnableAutoConfiguration y @ComponentScan mientras intentaba excluir clases de configuración específicas, ¡el problema es que no funcionó!

Eventualmente resolví el problema usando @SpringBootApplication , que según la documentación de Spring hace la misma funcionalidad que las tres anteriores en una anotación.

Otro consejo es intentar primero sin refinar el escaneo de paquetes (sin el filtro de paquetes base).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
Dorony
fuente
He intentado esto, pero aparece el error "Las siguientes clases no se pueden excluir porque no son clases de configuración automática". excludeFilters con @ComponentScan está funcionando bien.
AzarEJ
0

Necesitaba excluir un @Aspect @Component de auditoría del contexto de la aplicación, pero solo para algunas clases de prueba. Terminé usando @Profile ("auditoría") en la clase de aspecto; incluyendo el perfil para operaciones normales pero excluyéndolo (no lo ponga en @ActiveProfiles) en las clases de prueba específicas.

Miguel Pereira
fuente