如何实现弹簧限定符的 OR 逻辑?

2022-09-04 21:35:55

我有以下配置:

@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

所以,将被注入。bean2bean3

但是我想有逻辑的东西,所以我期望豆子和注射ORbean1bean2bean3bean4

我怎样才能实现它?

附言

@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;

你会得到一个和豆子的列表。myBean1myBean12

为此:

@Autowired private List<Marker2> myBeans;

你会得到一个和豆子的列表。myBean2myBean12

这行得通吗?

更新 I

定制工厂豆

我实现了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();
}

下面是一个所需标记的数组,其 bean 应处于选中状态,是已选择的 Bean 类型,并且是 Bean 的泛型类型的数组。最后一个参数是可选的,仅当 Bean 是通用的时才应使用。tagstypegenerics

然后,您可以将其与注释一起使用(否则Spring会注入所有类型的豆子):@QualifierSupplier<String>

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

Map 将包含三个 bean:和(它们的名称是 Map 的键,它们的实例是其值)。beanshihelloother

您可以在测试中找到更多使用示例。

更新二

自定义自动线候选解析器

由于@bhosleviraj推荐,我实现了TagaggedAutowireCandidateResolver,简化了自动布线所需bean的过程。只需使用相同的标签标记您的bean和自动连接的集合,您就可以将它们注入到集合中:

@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,并为其提供:CustomAutowireConfigurerTaggedAutowireCandidateResolver

@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;
    }
}

然后,在需要 OR 逻辑的所有 bean 中,您可以执行如下操作:

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

这会解决您的问题吗?

安杰洛


推荐