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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With