【问题标题】:exclude @Component from @ComponentScan从 @ComponentScan 中排除 @Component
【发布时间】: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 {}

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

如何排除?

【问题讨论】:

    标签: java spring dependency-injection spring-boot spring-ioc


    【解决方案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 中)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-19
        • 2018-06-14
        • 2019-11-14
        • 2018-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多