Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get bean from ApplicationContext by qualifier

Tags:

Given this code:

public interface Service {}  @Component @Qualifier("NotWanted") public class NotWantedService implements Service {}  @Component @Qualifier("Wanted") public class WantedService implements Service {}  AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(NotWantedService.class); ctx.register(WantedService.class); ctx.refresh() 

How do I now do:

ctx.getBean(Service.class) 

in a way that will only get the one with @Qualifier("Wanted") and not the one with @Qualifier("NotWanted")? I'm specifically asking if it's possible to do it using getBean, not injecting to a class, then using that one as a kind of proxy.

like image 699
levant pied Avatar asked Jan 14 '16 19:01

levant pied


People also ask

How do you get the BeanFactory reference in any class?

I can have my class implement BeanFactoryAware to get a reference to my beanfactory. Then I can do beanFactory. getBean("name"); to get access to a single bean.

What is the return type of getBean () method in Spring?

getBean. Return an instance, which may be shared or independent, of the specified bean. This method allows a Spring BeanFactory to be used as a replacement for the Singleton or Prototype design pattern. Callers may retain references to returned objects in the case of Singleton beans.


2 Answers

You can use

BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), Service.class, "Wanted") 

It's important to use ctx.getBeanFactory(), not ctx itself, because the 'qualifiedBeanOfType' method can resolve qualifiers only for ConfigurableListenableBeanFactory.

like image 107
Dmitry Ovchinnikov Avatar answered Nov 07 '22 06:11

Dmitry Ovchinnikov


It's not the @Qualifier annotation's purpose to use it when getting beans via ApplicationContext. But since you need such or similar functionality for some reasons, I suggest a workaround.

Create @Wanted and @NotWanted annotation:

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,         ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Wanted { } 

and

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,             ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface NotWanted { } 

Annotate your bean classes with these new annotations:

@Component @NotWanted public class NotWantedService implements Service {} 

and

@Component @Wanted public class WantedService implements Service {} 

Then you should add 2 methods somewhere where you have access to the ApplicationContext :

ApplicationContext applicationContext;  private <T> Collection<T>  getBeansByTypeAndAnnotation(Class<T> clazz, Class<? extends Annotation> annotationType){     Map<String, T> typedBeans = applicationContext.getBeansOfType(clazz);     Map<String, Object> annotatedBeans = applicationContext.getBeansWithAnnotation(annotationType);     typedBeans.keySet().retainAll(annotatedBeans.keySet());     return typedBeans.values(); }  private <T> Optional<T> getBeanByTypeAndAnnotation(Class<T> clazz, Class<? extends Annotation> annotationType) {     Collection<T> beans = getBeansByTypeAndAnnotation(clazz, annotationType);     return beans.stream().findFirst(); } 

And now you can use them to get beans or one bean by annotation and type like this:

Collection<Service> services = getBeansByTypeAndAnnotation(Service.class, Wanted.class); 

or

Service service = getBeanByTypeAndAnnotation(Service.class, Wanted.class); 

Possibly it's not the best way to deal with the problem. But since we are unable to get beans from ApplicationContext by qualifier and type 'out of box', that's one of the ways to do this.

like image 25
Rozart Avatar answered Nov 07 '22 06:11

Rozart