簡體   English   中英

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

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

如何使用帶有@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沒有被注冊為mbeans等。但是這個列表是可訪問的,所以我可以自動裝配一個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());
    }
}

但這只是一個非常丑陋的黑客。

為了注入你的MyBean列表,請嘗試@Resource而不是@Autowired。 例如

@Resource
public List<MyBean> myBeans

使用@Configuration為每個方法(AFAIK)定義多個bean是不可能的。 所以你必須繼續使用BFPP或使用ApplicationContect.getAutowireCapableBeanFactory().autowire(object);

我相信在這種情況下另一種選擇是以下列方式使用@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時才會調用帶注釋的方法。

如我錯了請糾正我

您可以使用支持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;
}

MyBeans不會進行后期處理,因為它們是使用new創建的,而不是由Spring容器初始化的。

您需要使用原型bean,每個組件發出的請求都有一個新bean的實例。

您將需要標記您的MyBean(Device device) bean聲明

@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

然后調用它而不是使用new填充beans

我最終擴展了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());
        }
    }
}

當然,這是hacky,但感覺不像提問者的方法那么丑陋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM