简体   繁体   中英

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. 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. 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. My question is can I at the point of injecting tell spring to exclude the class being injected to from the list of beans?

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. 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. 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.

So your Foo2 class would look like

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

@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. To look up, use any of the methods from BeanFactoryUtils , like beanNamesForTypeIncludingAncestors , and then exclude foo1 , which is the bean name for class Foo1 . The look up can even be coded into a default method in interface Foo .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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