简体   繁体   English

使用@Configuration在Spring中创建bean集合

[英]Creating a collection of beans in Spring using @Configuration

How can I create a collection of beans that will be properly managed by Spring using a class with a @Configuration annotation. 如何使用带有@Configuration批注的类来创建Spring正确管理的bean集合。

I would like to do something like this: 我想做这样的事情:

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

But the MyBean instances aren't post processed. 但MyBean实例未经过后期处理。 So their @Autowired methods are not called, the beans are not registered as mbeans and etc. The list is however accessible so that I can autowire a List of MyBean objects. 所以他们的@Autowired方法没有被调用,bean没有被注册为mbeans等。但是这个列表是可访问的,所以我可以自动装配一个MyBean对象列表。

I cannot use something like: 我不能使用像:

@Configuration
public Config {
    @Autowired
    private SomeConfiguration config;

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

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

Since the number of MyBean instances are not known before runtime. 由于在运行时之前不知道MyBean实例的数量。 The reason I want to do this is because we are controlling a physical machine that have a variable amount of components. 我想这样做的原因是因为我们正在控制具有可变数量组件的物理机器。 And I want to have one bean per component. 我希望每个组件有一个bean。

I'm currently achieving our goal by using a BeanFactoryPostProcessor like this: 我目前通过使用像这样的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());
    }
}

But this just feels like a really ugly hack. 但这只是一个非常丑陋的黑客。

In order to inject your MyBean list try @Resource instead of @Autowired. 为了注入你的MyBean列表,请尝试@Resource而不是@Autowired。 for eg 例如

@Resource
public List<MyBean> myBeans

It's not possible using @Configuration to define more than one bean per method (AFAIK). 使用@Configuration为每个方法(AFAIK)定义多个bean是不可能的。 So you will have to contnue using a BFPP or use ApplicationContect.getAutowireCapableBeanFactory().autowire(object); 所以你必须继续使用BFPP或使用ApplicationContect.getAutowireCapableBeanFactory().autowire(object);

I believe that another option in this case is to use @PostConstruct in the following manner: 我相信在这种情况下另一种选择是以下列方式使用@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));
        }   
    }
}

The @PostConstruct annotation is useful for initializing properties. @PostConstruct注释对初始化属性很有用。 It guarantees that the annotated method will only be called once when the bean is created. 它保证只有在创建bean时才会调用带注释的方法。

Correct me if I'm wrong 如我错了请纠正我

You can use the ConfigurableListableBeanFactory which supports SmartLifecycle so if you register the bean before your app is fully initialized it will call start() for you and other post processing steps. 您可以使用支持SmartLifecycleConfigurableListableBeanFactory ,因此如果您在应用程序完全初始化之前注册bean,它将为您和其他后处理步骤调用start()

However - if you call beanFactory.registerSingleton after spring has initialized you will manually need to call start() - on the bright side though you bean is still fully wired into the lifecycle management and spring will call stop() for you when the application context is shutdown. 但是 - 如果你在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;
}

MyBeans are not post processed as they are created with new and not initialised by the Spring container. MyBeans不会进行后期处理,因为它们是使用new创建的,而不是由Spring容器初始化的。

You need to use prototype beans, have an instance of a new bean per request made by a component. 您需要使用原型bean,每个组件发出的请求都有一个新bean的实例。

You will need to tag your MyBean(Device device) bean declaration like 您将需要标记您的MyBean(Device device) bean声明

@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Then call that instead of using new where you populate beans . 然后调用它而不是使用new填充beans

I've ended up extending the ArrayList. 我最终扩展了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());
        }
    }
}

This is, of course, hacky but feels a less uglier than the questioners approach. 当然,这是hacky,但感觉不像提问者的方法那么丑陋。

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

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