【问题标题】:Creating a collection of beans in Spring using @Configuration在 Spring 中使用 @Configuration 创建一个 bean 集合
【发布时间】:2011-05-26 20:27:48
【问题描述】:

如何使用带有 @Configuration 注释的类创建一个将由 Spring 正确管理的 bean 集合。

我想做这样的事情:

@Configuration
public Config {
    @Autowired
    private SomeConfiguration config;

    @Bean
    public List<MyBean> myBeans() {
        List<MyBean> beans = new ArrayList<MyBean>();
        for (Device device : config.getDevices()) {
            beans.add(new MyBean(device));
        }
        return beans;
    }
}

但是 MyBean 实例没有经过后处理。所以他们的@Autowired 方法没有被调用,bean 没有注册为 mbean 等等。但是该列表是可访问的,因此我可以自动装配 MyBean 对象的列表。

我不能使用类似的东西:

@Configuration
public Config {
    @Autowired
    private SomeConfiguration config;

    @Bean
    public MyBean myBean1() { ... }

    @Bean
    public MyBean myBean2() { ... }
}

因为 MyBean 实例的数量在运行前是未知的。我想这样做的原因是因为我们正在控制具有可变数量组件的物理机器。我希望每个组件有一个 bean。

我目前正在通过使用这样的 BeanFactoryPostProcessor 来实现我们的目标:

@Component
public class MyBeansFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Autowired
    private SomeConfiguration config;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeanException {
        for (Device device : config.getDevices()) {
            createAndRegister(BeanDefinitionRegistry) beanFactory, device);
        }
    }

    private void createAndRegister(BeanDefinitionRegistry registry, Device device) {
        register.registerBeanDefinition("device" + device.getId(), BeanDefinitionBuilder.genericBeanDefinition(MyBean.class).addConstructorArgValue(device).getBeanDefinition());
    }
}

但这感觉就像一个非常丑陋的黑客攻击。

【问题讨论】:

  • 在这个不太常见的用例上仍然没有可接受和好的答案吗?这太疯狂了。

标签: java spring configuration collections annotations


【解决方案1】:

为了注入您的 MyBean 列表,请尝试使用 @Resource 而不是 @Autowired。例如

@Resource
public List<MyBean> myBeans

【讨论】:

    【解决方案2】:

    您可以使用支持SmartLifecycleConfigurableListableBeanFactory,因此如果您在应用完全初始化之前注册bean,它将为您和其他后期处理步骤调用start()

    但是 - 如果你在 spring 初始化之后调用 beanFactory.registerSingleton,你将需要手动调用 start() - 虽然你的 bean 仍然完全连接到生命周期管理中,spring 会为你调用 stop()应用程序上下文已关闭。

    @Autowired
    private ConfigurableListableBeanFactory beanFactory;
    
    @Bean
    public List<MyBean> myBeansList() {
    
        List<MyBean> mylist; // Construct your list dynamically
    
        while(myCondition) {
            MyBean bean;
            // Manually register each instance with Spring
            beanFactory.registerSingleton("unique-name-for-this-bean",bean);
        }
    
        // Return your list as a bean so you can still autowire the list of beans
        // but each bean has already been manually added to the context
        return mylist;
    }
    

    【讨论】:

      【解决方案3】:

      我相信在这种情况下,另一种选择是通过以下方式使用@PostConstruct:

      @Configuration
      public Config {
      
          @Autowired
          private SomeConfiguration config;
      
          List<MyBean> beans = new ArrayList<MyBean>();
      
          @Bean
          public List<MyBean> myBeans() {     
      
              return beans;
          }
      
          @PostConstruct
          public void init() {
              for (Device device : config.getDevices()) {
                  beans.add(new MyBean(device));
              }   
          }
      }
      

      @PostConstruct 注解对于初始化属性很有用。它保证了被注解的方法只会在创建 bean 时被调用一次。

      如果我错了,请纠正我

      【讨论】:

        【解决方案4】:

        不可能使用@Configuration 为每个方法定义多个 bean (AFAIK)。因此,您将不得不继续使用 BFPP 或使用 ApplicationContect.getAutowireCapableBeanFactory().autowire(object);

        【讨论】:

        • 感谢这将确保调用所有 @Autowired 方法。但它不会向 JMX 注册 bean 并应用其他后处理。我也试过ApplicationContect.getAutowireCapableBeanFactory().applyBeanPostProcessorsBeforeInitialization(bean, beanName);ApplicationContect.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(bean, beanName); 但是由于没有bean定义,它们不会执行完整的后期处理。
        • @pcm 有道理,那么我猜你已经在用最好的方式做到了
        【解决方案5】:

        MyBeans 未经过后期处理,因为它们是使用 new 创建的,并且未由 Spring 容器初始化。

        您需要使用原型 bean,每个组件发出的请求都有一个新 bean 的实例。

        您将需要标记您的 MyBean(Device device) bean 声明,如

        @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

        然后调用它,而不是在填充 beans 的地方使用 new

        【讨论】:

          【解决方案6】:

          我最终扩展了 ArrayList。

          @Configuration
          public class Config {
              @Autowired
              private SomeConfiguration config;
          
              @Bean
              public List<MyBean> myBeans() {
                  List<MyBean> beans = new MyBeanList();
                  for (final Device device : config.getDevices()) {
                      beans.add(new MyBean(device));
                  }
                  return beans;
              }
          
              private static class MyBeanList extends ArrayList<MyBean> {
                  @PostConstruct
                  public void init() {
                      this.forEach(bean -> bean.init());
                  }
          
                  @PreDestroy
                  public void close() {
                      this.forEach(bean -> bean.close());
                  }
              }
          }
          

          当然,这很老套,但感觉不像提问者的方法那么难看。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-15
            • 1970-01-01
            • 1970-01-01
            • 2014-12-31
            相关资源
            最近更新 更多