Как я могу создать коллекцию beans, которой 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 не вызываются, bean-компоненты не регистрируются как mbeans и т. Д. Список, однако, доступен, так что я могу автоматически подключить List of 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());
}
}
Но это просто кажется действительно уродливым хаком.
Невозможно использовать @Configuration
для определения более одного компонента на метод (AFAIK). Таким образом, вам придется продолжить, используя 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 полезна для инициализации свойств. Это гарантирует, что аннотированный метод будет вызываться только один раз при создании компонента.
Поправь меня, если я ошибаюсь
В итоге я расширил список 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());
}
}
}
Это, конечно, хакерский, но чувствует себя менее уродливым, чем подход спрашивающих.