Ответ 1
Чтобы ввести свой список MyBean, попробуйте @Resource вместо @Autowired. например,
@Resource
public List<MyBean> myBeans
Как создать коллекцию 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 методы не вызываются, beans не регистрируются как mbeans и т.д. Однако список доступен для того, чтобы я мог autowire список объектов 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
для определения более чем одного bean для каждого метода (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 полезна для инициализации свойств. Это гарантирует, что аннотированный метод будет вызываться только один раз при создании компонента.
Поправьте меня если я ошибаюсь
MyBeans
не обрабатываются после обработки, поскольку они создаются с помощью new
и не инициализируются контейнером Spring.
Вам нужно использовать прототип beans, иметь экземпляр нового bean для каждого запроса, сделанного компонентом.
Вам нужно будет пометить ваше объявление MyBean(Device device)
bean, например
@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Затем вызовите это вместо использования new
, где вы заполняете beans
.
Вы можете использовать ConfigurableListableBeanFactory
которая поддерживает SmartLifecycle
поэтому, если вы зарегистрируете бин до полной инициализации приложения, он вызовет start()
для вас и других шагов постобработки.
Однако - если вы вызываете beanFactory.registerSingleton
после инициализации spring, вам нужно будет вручную вызвать start()
- с другой стороны, хотя ваш компонент все еще полностью подключен к управлению жизненным циклом, и 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;
}
В итоге я расширил 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());
}
}
}
Это, конечно, хакерство, но оно кажется менее уродливым, чем вопросники.