【问题标题】:How to implement OR logic for spring qualifiers?如何为弹簧限定符实现 OR 逻辑?
【发布时间】:2019-07-19 10:41:34
【问题描述】:

我有以下配置:

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

默认情况下,spring 为每个@Qualifier 使用AND 逻辑

所以bean2bean3 将被注入。

但我希望 OR 为这些东西提供逻辑,所以我希望豆 bean1 bean2 bean3bean4 被注入

我怎样才能实现它?

附言

@Qualifier 注释不可重复,因此我必须为每个注释创建元注释:

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

【问题讨论】:

  • 您确定描述正确吗? So bean2 and bean3 will be injectedso I expect beans bean1 bean2 bean3 and bean4 to be injected
  • @Eugene 你能详细说明一下吗? AR 是 所以 bean2 和 bean3 将被注入但我想要另一种行为
  • @Eugene 如果 bean 至少有一个匹配的限定符,我想注入 bean
  • 你是说如果某个基于QualifierX的已经加载到上下文中,你不希望另一个具有相同QualifierX的bean被加载到上下文中?
  • @Eugene 我想要所有的豆子

标签: java spring spring-boot autowired


【解决方案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 bean 的列表。

为此:

@Autowired private List<Marker2> myBeans;

您将获得myBean2myBean12 bean 的列表。

这行得通吗?

更新我

自定义 FactoryBean

我实现了TagsFactoryBean 类和@Tags 注释,您可以使用它们来解决您的任务(我希望:))。

首先,用@Tags注解标记你的bean:

@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 是一组应选择的bean 的期望标签,type 是一个选定的bean 类型,generics 是一组泛型类型的bean。最后一个参数是可选的,仅当您的 bean 是通用的时才应使用。

然后你可以将它与@Qualifier注解一起使用(否则Spring会注入所有Supplier&lt;String&gt;类型的bean):

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

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

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

更新 II

自定义 AutowireCandidateResolver

感谢@bhosleviraj recommendation,我实现了TaggedAutowireCandidateResolver,它简化了自动装配所需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";
   }
}

您不仅可以使用 Map 来注入 bean,还可以使用其他 Collections。

要让它工作,你必须在你的应用程序中注册一个CustomAutowireConfigurer bean 并提供TaggedAutowireCandidateResolver

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

更多用法示例见Test

【讨论】:

  • 我想通过几个标记自动装配。在您的情况下,我想要 Autowire 所有实现接口 Marker1、Marker2 的 bean。所以看起来你的方法不适用于我的主题
  • 我需要像@Autowired private List myBeans这样的东西
  • 在这种情况下,我必须为我需要的每个标签组合声明@Bean
  • @gstackoverflow 是问题还是陈述? )
  • 声明)
【解决方案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") 
}

这会解决您的问题吗?

安杰洛

【讨论】:

  • 我相信这会起作用,但这是一个糟糕的设计。您将在 spring 和您的业务逻辑之间紧密耦合
  • Yes.... 其他解决方案是创建自定义注释并使用 AOP 来创建您的 OR 逻辑
【解决方案3】:

回答需要深入了解 Spring 中如何实现自动装配解析,因此我们可以对其进行扩展。 我还没有想出任何解决方案,但我可以给你一些指示。 可能的扩展候选者是 QualifierAnnotationAutowireCandidateResolver ,覆盖方法解析为合格的 bean。并将自定义自动装配解析器传递给 bean 工厂。 您可以从这里克隆源代码和更正版本分支: https://github.com/spring-projects/spring-framework

spring-beans 模块中有一个CustomAutowireConfigurerTests,它可能会帮助你理解一些事情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多