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

发布于 2024-11-10 03:12:17 字数 1645 浏览 3 评论 0原文

如何使用带有 @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 不会注册为 mbean 等。但是该列表是可访问的,以便我可以自动装配 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());
    }
}

但这感觉就像一个非常丑陋的黑客。

How can I create a collection of beans that will be properly managed by Spring using a class with a @Configuration annotation.

I would like to do something like this:

@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;
    }
}

But the MyBean instances aren't post processed. So their @Autowired methods are not called, the beans are not registered as mbeans and etc. The list is however accessible so that I can autowire a List of MyBean objects.

I cannot use something like:

@Configuration
public Config {
    @Autowired
    private SomeConfiguration config;

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

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

Since the number of MyBean instances are not known before runtime. The reason I want to do this is because we are controlling a physical machine that have a variable amount of components. And I want to have one bean per component.

I'm currently achieving our goal by using a BeanFactoryPostProcessor like this:

@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());
    }
}

But this just feels like a really ugly hack.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

为你拒绝所有暧昧 2024-11-17 03:12:17

您可以使用支持 SmartLifecycleConfigurableListableBeanFactory,因此,如果您在应用完全初始化之前注册该 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;
}

You can use the ConfigurableListableBeanFactory which supports SmartLifecycle so if you register the bean before your app is fully initialized it will call start() for you and other post processing steps.

However - if you call beanFactory.registerSingleton after spring has initialized you will manually need to call start() - on the bright side though you bean is still fully wired into the lifecycle management and spring will call stop() for you when the application context is shutdown.

@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;
}
躲猫猫 2024-11-17 03:12:17

我相信在这种情况下,另一个选择是按以下方式使用 @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 时被调用一次。

如果我错了请纠正我

I believe that another option in this case is to use @PostConstruct in the following manner:

@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));
        }   
    }
}

The @PostConstruct annotation is useful for initializing properties. It guarantees that the annotated method will only be called once when the bean is created.

Correct me if I'm wrong

原来是傀儡 2024-11-17 03:12:17

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

@Resource
public List<MyBean> myBeans

In order to inject your MyBean list try @Resource instead of @Autowired. for e.g.

@Resource
public List<MyBean> myBeans
一直在等你来 2024-11-17 03:12:17

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

It's not possible using @Configuration to define more than one bean per method (AFAIK). So you will have to contnue using a BFPP or use ApplicationContect.getAutowireCapableBeanFactory().autowire(object);

毁梦 2024-11-17 03:12:17

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

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

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

@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

然后调用它而不是使用 new 填充 bean 的位置。

MyBeans are not post processed as they are created with new and not initialised by the Spring container.

You need to use prototype beans, have an instance of a new bean per request made by a component.

You will need to tag your MyBean(Device device) bean declaration like

@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Then call that instead of using new where you populate beans.

迎风吟唱 2024-11-17 03:12:17

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

当然,这有点老套,但感觉比提问者的方法要好一些。

I've ended up extending the 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());
        }
    }
}

This is, of course, hacky but feels a less uglier than the questioners approach.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文