2016-01-14 1 views
7

Учитывая этот код:Получить боб от ApplicationContext по классификатором

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() 

Как сейчас делают:

ctx.getBean(Service.class) 

таким образом, что будет только один с @Qualifier("Wanted"), а не тот, у кого @Qualifier("NotWanted") ? Я специально спрашиваю, можно ли это сделать, используя getBean, а не впрыскивать в класс, а затем использовать его как своего рода прокси.

+0

Почему бы не получить bean по имени, так как вы используете постоянное имя, то есть 'ctx.getBean (« Wanted »)'? – aux

+0

@aux У вас есть 50 мест, где вы делаете 'ctx.getBean (" service1 ")', и теперь вы хотите изменить его на 'ctx.getBean (" service2 ")'. Это 50 изменений. Изменение классификатора - это изменение только на 2 определения bean ('service1' и' service2'). Есть и другие случаи - скажем, я хочу получить несколько экземпляров 'Service', которые' Wanted'. Они не могут иметь одинаковое имя. –

+0

ОК, я вижу. Тогда как насчет введения собственного «реестра», который содержит ссылки на ваши бобы и используется для поиска по различным параметрам, например «Репозитории» в режиме «весенние данные»? Или фасоль-обертка? – aux

ответ

7

Это не цель аннотации @Qualifier, чтобы использовать ее при получении bean-компонентов через ApplicationContext. Но так как вам нужны такие или подобные функции по некоторым причинам, я предлагаю обходной путь.

Создать @Wanted и @NotWanted аннотация:

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

и

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

аннотирования классы боба с этими новыми аннотациями:

@Component 
@NotWanted 
public class NotWantedService implements Service {} 

и

@Component 
@Wanted 
public class WantedService implements Service {} 

Затем вы должны добавить 2 метода где-то, где у вас есть доступ к 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(); 
} 

И теперь вы можете использовать их, чтобы получить бобы или один боб с помощью аннотаций и типа, как это:

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

или

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

Возможно, это не самый лучший способ DEA с этой проблемой. Но так как мы не можем получить beans из ApplicationContext с помощью квалификатора и введите «из коробки», это один из способов сделать это.

+1

Спасибо @ Rozart - Я уже это пробовал. К сожалению, это не учитывает '@ Qualifier'. Он принимает имя компонента, заданного с помощью '@Bean (name =" abc ")', что не является одинаковым. –

+0

Это то же самое, что и '@Bean (name =" abc ")', хотя у него нет той же семантики. У вас не может быть двух бобов по одному и тому же, но вы можете иметь более одного квалифицированного одинакового. То есть, я хочу, чтобы 10 различных услуг были квалифицированы с помощью «Wanted», но совсем не назывались «Wanted». –

+0

Я обновил ответ. Может быть, это поможет вам. :) – Rozart

2

Если вы хотите получить bean из контекста, не впрыскивающего, лучший способ определить имя bean в аннотации @Component и получить его по имени из контекста. В большинстве случаев @Qualifier используется для инъекций.

1

Ближайший канонический способ сделать это весной - это класс полезности BeanFactoryAnnotationUtils ... но это, к сожалению, работает только с аргументом значения аргумента примечания @Qualifier (следовательно, почему аргумент является строкой).

Что такое @Rozart is recommending - это лучший подход, и действительно что-то подобное должно быть в BeanFactoryAnnotationUtils. Я включил этот ответ только в том случае, если кто-то приземлился здесь и хочет напрямую использовать @Qualifier (и все сглаживание фасоли, которое приходит вместе с ним).

Я рекомендую подать заявку на функцию весной (я бы подумал, что они, возможно, устали от меня, прогоняя их :-)).

4

Вы можете использовать

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

Это важно использовать ctx.getBeanFactory(), а не сам ctx, так как метод «qualifiedBeanOfType» может разрешить отборочные только для ConfigurableListenableBeanFactory.

Смежные вопросы