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

时间:2011-05-26 20:27:49

标签: java spring configuration collections annotations

如何使用带有@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());
    }
}

但这只是一个非常难看的黑客。

6 个答案:

答案 0 :(得分:4)

为了注入MyBean列表,请尝试@Resource而不是@Autowired。例如。

@Resource
public List<MyBean> myBeans

答案 1 :(得分:2)

使用@Configuration每个方法(AFAIK)定义多个bean是不可能的。因此,您必须使用BFPP或使用ApplicationContect.getAutowireCapableBeanFactory().autowire(object);

答案 2 :(得分:0)

我认为在这种情况下另一种选择是以下列方式使用@PostConstruct:

// export.js
import { schema } from './schema.js'
import { graphql, introspectionQuery, printSchema } from 'graphql';

// Save json schema
graphql(schema, introspectionQuery).then(result => {
  fs.writeFileSync(
    `${yourSchemaPath}.json`,
    JSON.stringify(result, null, 2)
  );
});

// Save user readable type system shorthand of schema
fs.writeFileSync(
  `${yourSchemaPath}.graphql`,
  printSchema(schema)
);

@PostConstruct注释对初始化属性很有用。它保证只有在创建bean时才会调用带注释的方法。

如果我错了,请纠正我

答案 3 :(得分:0)

MyBeans没有进行后处理,因为它们是使用new创建的,而不是由Spring容器初始化的。

您需要使用原型bean,每个组件请求都有一个新bean的实例。

您需要标记MyBean(Device device) bean声明,如

@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

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

答案 4 :(得分:0)

您可以使用支持ConfigurableListableBeanFactory的{​​{1}},因此,如果在应用程序完全初始化之前注册Bean,它将为您和其他后期处理步骤调用SmartLifecycle

但是-如果您在spring初始化之后调用start(),则您将需要手动调用beanFactory.registerSingleton-在积极的一面,尽管您的bean仍完全连接到生命周期管理中,并且spring会调用{{ 1}}在关闭应用程序上下文时为您服务。

start()

答案 5 :(得分:0)

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

这当然是很棘手的,但是比发问者的方法要丑陋得多。