简体   繁体   English

在@Configuration中注入合并的bean列表

[英]Inject a merged list of beans within @Configuration

First, let me introduce a few simple classes and interface to be able to describe my problem. 首先,让我介绍一些简单的类和接口来描述我的问题。

interface Basic { void foo(); }
interface Extended extends Basic { void bar(); }

class BasicService {
  @Inject
  List<Basic> basics;

  void execute() {
    basics.forEach(Basic::foo);
  }
}

class ExtendedService {
  @Inject
  List<Extended> extendeds;

  void execute() {
    extendeds.forEach(Extended::bar);
  }
}

@Configuration
class MyConfiguration {
  // Assume, that Basic1 and Basic2 are implementations of Basic and
  // Extended1 is an implementation of Extended
  @Bean
  public Basic basic1() {
    return new Basic1();
  }
  @Bean
  public Basic basic2() {
    return new Basic2();
  }
  @Bean
  public List<Extended> extendeds() {
    return Arrays.asList(new Extended1("0"), new Extended1("1"), new Extended1("2"));
  }

  @Bean
  public BasicService basicService() {
    return new BasicService();
  }
  @Bean
  public ExtendedService extendedService() {
    return new ExtendedService();
  }
}

I have two services that act on different level of abstraction. 我有两个服务,它们作用于不同的抽象级别。 My problem is that I'm failing to find a way how I can inject all beans that implement the Basic interface in my BasicService . 我的问题是我无法找到一种方法来注入所有在BasicService中实现Basic接口的BasicService With the current implementation it only injects all Extended implementations, because I have a factory bean method that has a return type of List in its method signature. 对于当前的实现,它仅注入所有Extended实现,因为我有一个工厂bean方法,其方法签名中具有List的返回类型。

I can not change the bean configuration in a way that all Extended beans have their own factory methods, because in my real code the number of Extended implementations is dynamically computed on runtime... 我无法以所有Extended bean都有自己的工厂方法的方式更改bean配置,因为在我的真实代码中, Extended实现的数量是在运行时动态计算的...

Is there a way I can configure Spring, so that it merges all beans with Basic and List<Basic> together in 1 big list that I can use in my BasicService ? 有没有一种方法可以配置Spring,以便将所有具有BasicList<Basic> bean合并到一个可以在BasicService使用的大列表中?

I'm not sure whether there is a way to solve this implicitly, since Spring tries to find the best match, which the List bean seems to be (have you tried generics, eg, List<? extends Basic> ?). 我不确定是否有一种隐式解决此问题的方法,因为Spring试图找到List Bean似乎是的最佳匹配项(您是否尝试过泛型,例如List<? extends Basic> ?)。

But you could do it programmatically, by getting the bean definitions from the ApplicationContext : 但是您可以通过从ApplicationContext获取Bean定义来以编程方式进行操作:

public BasicService(@Inject ApplicationContext ctx) {
    Map<String, ? extends Basic> basicMap = ctx.getBeansOfType(Basic.class);
    Map<String, Collection<? extends Basic>> basicCollectionMap = ctx.getBeansofType(ResolvableType.forClassWithGenerics(Collection.class, Basic.class));

    // Now merge
    Collection<? extends Basic> basics = basicMap.values();
    basicCollectionMap.values().stream().map(l -> basics.addAll(l));
}

I just typed this here, so I hope there aren't any obvious errors. 我只是在这里输入,因此希望没有明显的错误。

I solved the problem by adding generic bean definitions for the Extended implementations. 我通过为Extended实现添加通用bean定义解决了这个问题。 This can be done by adding a BeanDefinitionRegistryPostProcessor implementation to the @Configuration class as follows: 这可以通过将BeanDefinitionRegistryPostProcessor实现添加到@Configuration类来完成,如下所示:

@Configuration
class MyConfiguration {
  @Bean
  public static ExtendedBeanFactory extendedBeanFactory() {
    return new ExtendedBeanFactory();
  }
}

class ExtendedBeanFactory implements BeanDefinitionRegistryPostProcessor {
  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    for (int i = 0; i < 3; i++) {
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setBeanClass(Extended1.class);
      ConstructorArgumentValues args = new ConstructorArgumentValues();
      args.addGenericArgumentValue(Integer.toString(i));
      beanDefinition.setConstructorArgumentValues(args);
      registry.registerBeanDefinition("extended_" + i, beanDefinition);
    }
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // noop
  }
}

This has the advantage, that consumer classes can simply inject a list of Basic or a list of Extended as the code from the question does. 这样做的好处是,使用者类可以像问题代码一样简单地插入Basic列表或Extended列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM