Создание коллекции bean-компонентов в Spring с использованием @Configuration - PullRequest
17 голосов
/ 27 мая 2011

Как я могу создать коллекцию bean-компонентов, которая будет правильно управляться Spring, используя класс с аннотацией @Configuration.

Я хотел бы сделать что-то вроде этого:

@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 не вызываются, бины не регистрируются как mbeans и т. Д. Однако этот список доступен, так что я могу автоматически связывать список объектов MyBean.

Я не могу использовать что-то вроде:

@Configuration
public Config {
    @Autowired
    private SomeConfiguration config;

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

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

Поскольку количество экземпляров MyBean не известно до времени выполнения.Причина, по которой я хочу это сделать, заключается в том, что мы контролируем физическую машину с переменным количеством компонентов.И я хочу, чтобы по одному компоненту на компонент.

В настоящее время я достигаю нашей цели, используя 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());
    }
}

Но это похоже на действительно уродливый хак.

Ответы [ 6 ]

4 голосов
/ 02 января 2014

Для добавления вашего списка MyBean попробуйте @Resource вместо @Autowired. например,

@Resource
public List<MyBean> myBeans
2 голосов
/ 27 мая 2011

Невозможно использовать @Configuration для определения более одного компонента на метод (AFAIK). Таким образом, вам придется продолжать использовать BFPP или использовать ApplicationContect.getAutowireCapableBeanFactory().autowire(object);

1 голос
/ 31 августа 2018

Вы можете использовать ConfigurableListableBeanFactory, который поддерживает SmartLifecycle, поэтому, если вы зарегистрируете бин до того, как ваше приложение будет полностью инициализировано, он вызовет start() для вас и другие этапы постобработки.

Однако - если вы позвоните beanFactory.registerSingleton после инициализации пружины, вам нужно будет вручную позвонить start() - на светлой стороне, хотя ваш боб все еще полностью подключен к управлению жизненным циклом, и Spring вызовет для вас stop(), когда контекст приложения выключен.

* * 1010
1 голос
/ 01 августа 2017

Я считаю, что другой вариант в этом случае - использовать @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 полезна для инициализации свойств.Это гарантирует, что аннотированный метод будет вызываться только один раз при создании компонента.

Исправьте меня, если я ошибаюсь

0 голосов
/ 18 апреля 2019

Я закончил расширением 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());
        }
    }
}

Это, конечно, хакерство, но кажется менее уродливым, чем подход спрашивающих.

0 голосов
/ 01 августа 2017

MyBeans не подвергаются пост-обработке, так как они создаются с помощью new и не инициализируются контейнером Spring.

Вам необходимо использовать компоненты-прототипы, иметь экземпляр нового компонента на запрос, сделанныйкомпонент.

Вам нужно будет пометить ваше объявление MyBean(Device device) bean как

@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Затем вызовите это вместо использования new, где вы заполняете beans.

...