onkami
onkami

Reputation: 9411

Spring: get all Beans of certain interface AND type

In my Spring Boot application, suppose I have interface in Java:

public interface MyFilter<E extends SomeDataInterface> 

(a good example is Spring's public interface ApplicationListener< E extends ApplicationEvent > )

and I have couple of implementations like:

@Component
public class DesignatedFilter1 implements MyFilter<SpecificDataInterface>{...}

@Component
public class DesignatedFilter2 implements MyFilter<SpecificDataInterface>{...}

@Component
public class DesignatedFilter3 implements MyFilter<AnotherSpecificDataInterface>{...}

Then, in some object I am interested to utilize all filters that implement MyFilter< SpecificDataInterface > but NOT MyFilter< AnotherSpecificDataInterface >

What would be the syntax for this?

Upvotes: 93

Views: 92046

Answers (6)

Rajendra Thorat
Rajendra Thorat

Reputation: 3438

For my requirement accepted answer not suitable. So I injected Application context //Or use constructor injection as good practice.

 @Autowired
 private ApplicationContext applicationContext;

And then

Map<String, SomeClass> someClassTypeServices= applicationContext.getBeansOfType(SomeClass.class);

Upvotes: 0

Karthik
Karthik

Reputation: 21

Make public static map of singleton factory beans so that it can be accessed across all packages

@Component
public class ProcessorFactory {
public static Map<String, MyFilter > factory = new HashMap<>();

@Autowired
public void CreateFilterFactory(ListableBeanFactory beanFactory) {
    Collection< MyFilter> interfaces = beanFactory.getBeansOfType(MyFilter.class).values();
    interfaces.forEach(filter -> factory.put(filter.getSource(), filter));
}
}

Upvotes: 0

Issam El-atif
Issam El-atif

Reputation: 2486

You can simply use

@Autowired
private List<MyFilter<SpecificDataInterface>> filters;

Edit 7/28/2020:

As Field injection is not recommended anymore Constructor injection should be used instead of field injection

With constructor injection:

class MyComponent {

  private final List<MyFilter<SpecificDataInterface>> filters;

  public MyComponent(List<MyFilter<SpecificDataInterface>> filters) {
    this.filters = filters;
  }
  ...
}

Upvotes: 28

magiccrafter
magiccrafter

Reputation: 5474

In case you want a Map<String, MyFilter>, where the key (String) represents the bean name:

private final Map<String, MyFilter> services;

public Foo(Map<String, MyFilter> services) {
  this.services = services;
}

which is the recommended alternative to:

@Autowired
private Map<String, MyFilter> services;

Upvotes: 11

Quang Nguyen
Quang Nguyen

Reputation: 144

In case you want a map, below code will work. The key is your defined method

private Map<String, MyFilter> factory = new HashMap<>();

@Autowired
public ReportFactory(ListableBeanFactory beanFactory) {
  Collection<MyFilter> interfaces = beanFactory.getBeansOfType(MyFilter.class).values();
  interfaces.forEach(filter -> factory.put(filter.getId(), filter));
}

Upvotes: 11

mh-dev
mh-dev

Reputation: 5503

The following will inject every MyFilter instance that has a type that extends SpecificDataInterface as generic argument into the List.

@Autowired
private List<MyFilter<? extends SpecificDataInterface>> list;

Upvotes: 146

Related Questions