简体   繁体   English

按接口自动装配 bean,但在注入时排除特定 bean

[英]Autowire beans by interface but exclude specific bean at injection time

I'm currently injecting a list of Spring @Component which all implement the same interface.我目前正在注入一个 Spring @Component列表,它们都实现了相同的接口。 This works fine however I want to inject this same list into an instance of a bean in that list so I can act on all the other beans in that list.这很好用,但是我想将这个相同的列表注入到该列表中的一个 bean 的实例中,这样我就可以对该列表中的所有其他 bean 采取行动。 I can't see how to exclude a specific bean from that injection, I can filter the list after injecting it but this of course results in a Spring circular dependency exception at startup.我看不到如何从该注入中排除特定的 bean,我可以在注入后过滤列表,但这当然会在启动时导致 Spring 循环依赖异常。 My question is can I at the point of injecting tell spring to exclude the class being injected to from the list of beans?我的问题是我可以在注入时告诉 spring 从 bean 列表中排除注入的 class 吗?

public interface Foo {
    String doSomething();
}
@Component
public class Foo1 implements Foo {

    private final List<Foo> foos;

    public Sample(final List<Foo> foos) {
        //Don't include Foo1
        this.foos = foos;
    }

    public String doSomething() {
        foos.forEach(foo -> foo.doSomething());
        return "aString";
    }
}

@Component
public class Foo2 implements Foo {

    private final List<Foo> foos;

    public Sample(final List<Foo> foos) {
        //Don't include Foo2
        this.foos = foos;
    }

    public String doSomething() {
        foos.forEach(foo -> foo.doSomething());
        return "anotherString";
    }
}
@Component
public class Foo3 implements Foo {
//and so on

I agree with @robertobatts, it really looks like an antipattern and you simply should avoid it.我同意@robertobatts 的观点,它看起来确实像一个反模式,你应该避免它。 If you could clarify the problem context maybe we can help you out with a better approach for what you would like to achieve!如果您可以澄清问题背景,也许我们可以帮助您以更好的方法来实现您想要实现的目标!

This solution is pretty much a hack, and I would not recommend using it unless really necessary.这个解决方案几乎是一个 hack,除非真的有必要,否则我不建议使用它。 Instead of declaring Foo2 as a component you can define it inside a @Configuration class by injecting ListableBeanFactory as a parameter in the bean definition method and then using it's getBeansOfType method to filter out the implementations of Foo that you actually need to instantiate the Foo2 bean.您可以在@Configuration class 中将其定义为 @Configuration Foo2 ,而不是将其声明为组件,方法是将ListableBeanFactory作为 bean 定义方法中的参数注入,然后使用它的getBeansOfType方法过滤掉您实际需要实例化Foo2 bean 的Foo实现。

So your Foo2 class would look like所以你的 Foo2 class 看起来像

public class Foo2 implements Foo {

    private final List<Foo> foos;

    public Foo2(final List<Foo> foos) {
        this.foos = foos;
    }

    public String doSomething() {
        foos.forEach(foo -> foo.doSomething());
        return "anotherString";
    }
}

And the configuration class would look like并且配置 class 看起来像

@Configuration
public class MyConfiguration {

    @Bean
    Foo2 foo2(ListableBeanFactory bf) {
        final Map<String, Foo> fooBeans = bf.getBeansOfType(Foo.class);
        List<Foo> foos = // filter out what you don't need from fooBeans
        return new Foo2(foos);
    }

}

Instead of injecting a list in the constructor, you can look up all beans of type Foo in the doSomething methods, and then exclude the current one.您可以在doSomething方法中查找所有Foo类型的 bean,然后排除当前的,而不是在构造函数中注入列表。 To look up, use any of the methods from BeanFactoryUtils , like beanNamesForTypeIncludingAncestors , and then exclude foo1 , which is the bean name for class Foo1 .要查找,请使用BeanFactoryUtils中的任何方法,例如beanNamesForTypeIncludingAncestors ,然后排除foo1 ,这是 class Foo1的 bean 名称。 The look up can even be coded into a default method in interface Foo .查找甚至可以编码为接口Foo中的默认方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM