Как реализовать логику OR для квалификаторов Spring?

У меня есть следующая конфигурация:

@Qualifier1
@Qualifier2
@Bean
public MyBean bean1(){...}

@Qualifier2
@Qualifier3
@Bean
public MyBean bean2(){...}

@Qualifier1
@Qualifier2
@Qualifier3
@Bean
public MyBean bean3(){...}

@Qualifier3
@Bean
public MyBean bean4(){...}

@Qualifier1
@Bean
public MyBean bean5(){...}

И это место инъекции:

@Qualifier2
@Qualifier3
@Autowired:
private List<MyBean> beans;

По умолчанию пружина использует логику AND для каждого @Qualifier

Так что bean2 и bean3 будут введены.

Но я хочу иметь логику OR для этого материала, поэтому я ожидаю, что бины bean1 bean2 bean3 и bean4 будут внедрены

Как мне этого добиться?

P.S.

@Qualifier аннотация не повторяется, поэтому мне нужно создать метааннотацию для каждой аннотации:

@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Qualifier1 {
}

Ответы

Ответ 1

Что если вы использовали маркерные интерфейсы вместо квалификаторов? Например:

public class MyBean1 extends MyBean implements Marker1 {}

public class MyBean2 extends MyBean implements Marker2 {}

public class MyBean12 extends MyBean implements Marker1, Marker2 {}

Затем с помощью этого:

@Bean
public MyBean1 myBean1() {
    //...
}

@Bean
public MyBean2 myBean2() {
    //...
}

@Bean
public MyBean12 myBean12() {
    //...
}

и это:

@Autowired private List<Marker1> myBeans;

Вы получите список бинов myBean1 и myBean12.

И для этого:

@Autowired private List<Marker2> myBeans;

Вы получите список бинов myBean2 и myBean12.

Будет ли это работать?

ОБНОВЛЕНИЕ I

Custom FactoryBean

Я реализовал класс TagsFactoryBean и аннотацию @Tags, которые вы можете использовать для решения своей задачи (я надеюсь :)).

Во-первых, пометьте свои бобы аннотацией @Tags:

@Tags({"greeting", "2letters"})
@Bean
public Supplier<String> hi() {
    return () -> "hi";
}

@Tags({"parting", "2letters"})
@Bean
public Supplier<String> by() {
    return () -> "by";
}

@Tags("greeting")
@Bean
public Supplier<String> hello() {
    return () -> "hello";
}

@Tags("parting")
@Bean
public Supplier<String> goodbye() {
    return () -> "goodbye";
}

@Tags("other")
@Bean
public Supplier<String> other() {
    return () -> "other";
}

Затем подготовьте TagsFactoryBean:

@Bean
public TagsFactoryBean words() {
    return TagsFactoryBean.<Supplier>builder()
            .tags("greeting", "other")
            .type(Supplier.class)
            .generics(String.class)
            .build();
}

Здесь tags - это массив желаемых тегов, чьи бины должны быть выбраны, type - это выбранный тип бинов, а generics - это массив универсальных типов бинов. Последний параметр является необязательным и должен использоваться только в том случае, если ваши компоненты являются общими.

Затем вы можете использовать его с аннотацией @Qualifier (в противном случае Spring вводит все компоненты типа Supplier<String>):

@Autowired
@Qualifier("words")
private Map<String, Supplier<String>> beans;

Карта beans будет содержать три bean-компонента: hi, hello и other (их имена являются ключами карты, а их экземпляры - ее значениями).

Дополнительные примеры использования вы можете найти в тестах.

ОБНОВЛЕНИЕ II

Пользовательский AutowireCandidateResolver

Благодаря рекомендации @bhosleviraj recommendation я реализовал TaggedAutowireCandidateResolver, который упрощает процесс автоматической разметки нужных бинов. Просто пометьте свои бобы и коллекцию с автопроводкой одинаковыми тегами, и вы добавите их в коллекцию:

@Autowired
@Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;

@Configuration
static class Beans {
   @Tags({"greeting", "2symbols", "even"})
   @Bean
   public Supplier<String> hi() {
      return () -> "hi";
   }

   @Tags({"parting", "2symbols", "even"})
   @Bean
   public Supplier<String> by() {
      return () -> "by";
   }

   @Tags({"greeting", "5symbols", "odd"})
   @Bean
   public Supplier<String> hello() {
      return () -> "hello";
   }

   @Tags({"parting", "7symbols", "odd"})
   @Bean
   public Supplier<String> goodbye() {
      return () -> "goodbye";
   }

   @Tags({"other", "5symbols", "odd"})
   @Bean
   public Supplier<String> other() {
      return () -> "other";
   }
}

Вы можете использовать не только Карту для инъекций бобов, но и другие Коллекции.

Чтобы заставить его работать, вы должны зарегистрировать bean-компонент CustomAutowireConfigurer в своем приложении и предоставить ему TaggedAutowireCandidateResolver:

@Configuration
public class AutowireConfig {
   @Bean
   public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
      CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
      beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
      configurer.postProcessBeanFactory(beanFactory);
      return configurer;
   }
}

Дополнительные примеры использования см. в этом тесте.

Ответ 2

Я думаю, что вы не можете сделать это с помощью аннотации.

Я бы использовал org.springframework.context.ApplicationContextAware. Возможно, вам нужно написать дополнительный код, но таким образом вы сможете решить свою проблему.

Я бы реализовал такой класс:

@Component
public class SpringContextAware implements ApplicationContextAware {
    public static ApplicationContext ctx;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;
    }
    public static synchronized ApplicationContext getCtx() {
        return ctx;
    }
}

Затем во всех компонентах, где вам нужна логика ИЛИ, вы можете сделать что-то вроде этого:

@Autowired
private SpringContextAware ctxAware;
@PostConstruct
public void init() {
    //Here you can do your OR logic
    ctxAware.getCtx().getBean("qualifier1") or ctxAware.getCtx().getBean("qualifier2") 
}

Это решит вашу проблему?

Angelo

Ответ 3

Ответ требует глубокого понимания того, как разрешение автопроводки реализовано в Spring, чтобы мы могли его расширить. Я пока не могу найти никакого решения, но могу дать вам несколько советов. Возможный вариант расширения: QualifierAnnotationAutowireCandidateResolver, метод переопределения, который разрешается в квалифицированный компонент. И передайте пользовательский преобразователь autowire фабрике бобов. Вы можете клонировать исходный код и исправить версию ветки отсюда: https://github.com/spring-projects/spring-framework

В модуле spring-beans есть CustomAutowireConfigurerTests, который может помочь вам понять несколько вещей.