从 @ComponentScan 中排除 @Component

Posted

技术标签:

【中文标题】从 @ComponentScan 中排除 @Component【英文标题】:exclude @Component from @ComponentScan 【发布时间】:2013-09-30 08:01:36 【问题描述】:

我有一个组件要从特定@Configuration 中的@ComponentScan 中排除:

@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.CUSTOM, value=ExcludeFooFilter.class))
public class MySpringConfiguration 

但这似乎并没有像我想要的那样从扫描中排除组件。

如何排除?

【问题讨论】:

【参考方案1】:

配置看起来没问题,除了你应该使用excludeFilters而不是excludes

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

【讨论】:

抱歉,这是一个剪切和粘贴错误。我正在使用排除过滤器。我再看一下,这给我的错误真的很奇怪...... 就我而言,FilterType.ASSIGNABLE_TYPE 不起作用 - 我认为它仅在您要排除的类也是 @Configuration 类时才起作用。但是 FilterType.ASPECTJ 有效 - 特别是它甚至可以工作,例如对于单个类 - 不必引用包。此外,如果您的类层次结构中有多个@Configuration 类,则必须将排除过滤器应用于主@SpringBootApplication 类(这也是@Configuration) - 否则它会进行组件扫描并设法在之前实例化类其他过滤器开始工作。【参考方案2】:

在扫描过滤器中使用显式类型对我来说很难看。我相信更优雅的方法是创建自己的标记注释:

@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreDuringScan 

标记应与它一起排除的组件:

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

并从您的组件扫描中排除此注释:

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

【讨论】:

这是一个通用排除的聪明想法,但如果您只想从项目中的应用程序上下文的子集中排除组件,这将无济于事。真的,要普遍排除它,可以删除@Component,但我认为这不是问题所在 如果您在某个地方有另一个组件扫描注释但没有相同的过滤器,这将不起作用 @Bashar Ali Labadi,这不是这个结构的重点吗?如果你想从所有组件扫描中排除它,它可能根本不应该是 Spring 组件。【参考方案3】:

另一种方法是使用新的条件注释。 从 plain 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 工厂。例如当“Bar”组件没有注册为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 用于条件,这对我来说比使用过滤器更干净。我从来没有让过滤器像条件加载 bean 那样始终如一地工作【参考方案4】:

如果您需要定义两个或多个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”【参考方案5】:

在尝试排除特定配置类时,我在使用 @Configuration@EnableAutoConfiguration@ComponentScan 时遇到了问题,问题就是这样没用!

最终我通过使用@SpringBootApplication 解决了这个问题,根据 Spring 文档,它在一个注释中与上述三个功能相同。

另一个提示是先尝试不优化包扫描(不使用 basePackages 过滤器)。

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

【讨论】:

我已经尝试过了,但显示错误为“无法排除以下类,因为它们不是自动配置类”。带有@ComponentScan 的 excludeFilters 工作正常。 这是错误的。 Foo 只能是自动配置类。【参考方案6】:

在排除测试组件或测试配置的情况下,Spring Boot 1.4 引入了新的测试注解@TestComponent and @TestConfiguration

【讨论】:

【参考方案7】:

我需要从应用程序上下文中排除审计@Aspect @Component,但仅适用于少数测试类。我最终在方面类上使用了 @Profile("audit") ;包括用于正常操作的配置文件,但在特定测试类中排除它(不要放在@Act​​iveProfiles 中)。

【讨论】:

以上是关于从 @ComponentScan 中排除 @Component的主要内容,如果未能解决你的问题,请参考以下文章

Springboot 排除不想加载的配置

spring boot增加@ComponentScan后就扫描不到其他包了

Spring注解驱动开发自定义TypeFilter指定@ComponentScan注解的过滤规则

Spring注解驱动开发自定义TypeFilter指定@ComponentScan注解的过滤规则

springboot 启动排除某些bean 的注入

Springboot启动时排除一些不需要注入的组件