如何使用带有@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
为每个方法定义多个bean(AFAIK)是不可能的。所以你必须使用BFPP或使用ApplicationContect.getAutowireCapableBeanFactory().autowire(object);
您可以使用支持ConfigurableListableBeanFactory
的SmartLifecycle
,因此如果您在应用程序完全初始化之前注册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;
}
我相信在这种情况下另一种选择是以下列方式使用@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时才会调用带注释的方法。
如我错了请纠正我
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,但感觉不像提问者的方法那么丑陋。