Get bean from ApplicationContext using classifier

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 should I do it now:

 ctx.getBean(Service.class) 

in a way that only @Qualifier("Wanted") will get, and not the one that has @Qualifier("NotWanted") ? I specifically ask if this is possible with getBean , and not for injection into a class, and then use it as a kind of proxy.

+7
spring
source share
4 answers

This is not the purpose of @Qualifier annotation to use it when getting beans through ApplicationContext. But since you need these or similar functions for some reason, I offer a workaround.

Create the annotation @Wanted and @NotWanted :

 @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 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 a single bean by annotation and type something like this:

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

or

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

This may not be the best way to deal with the problem. But since we cannot get beans from ApplicationContext using the qualifier and type out of the box, this is one way to do this.

+7
source share

you can use

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

It is important to use ctx.getBeanFactory() rather than ctx , because the qualifiedBeanOfType method can only allow qualifiers for ConfigurableListenableBeanFactory.

+4
source share

If you want to get a bean from a non-injecting context, it's best to define the bean name in the @Component annotation and get it by name from the context. In most cases, @Qualifier is used for injection.

+2
source share

The closest canonical way to do this in Spring is with the BeanFactoryAnnotationUtils utility class ... but this, unfortunately, only works with the @Qualifier value @Qualifier annotation itself (hence why the argument is a string).

What @Rozart recommends is the best approach, and really something like this should be in BeanFactoryAnnotationUtils . I included this answer only if someone has landed here and wants to use @Qualifier directly (and all the smoothing bean that comes with it).

I recommend filing a function request in Spring (I would, but I think they can get tired of me by running them :-)).

+1
source share

All Articles