如何在 Spring Boot 中以编程方式创建 bean? [英] How do I create beans programmatically in Spring Boot?

查看:42
本文介绍了如何在 Spring Boot 中以编程方式创建 bean?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,其中包含许多列在 application.properties 中的数据源设置.我有一个加载这些设置的 @ConfigurationProperties 类.现在我想从这个 ConfigurationProperties 类中获取值,并使用它们即时创建数据源 bean.我试过使用 @PostConstruct 并实现 BeanFactoryPostProcessor.然而,使用 BeanFactoryPostProcessor,处理似乎发生在我的 ConfigurationProperties 类被填充之前.如何使用 Spring Boot 即时读取属性和创建 DataSource bean?

I have an app that has a number of datasource settings listed in application.properties. I have a @ConfigurationProperties class that loads up these settings. Now I want to take the values from this ConfigurationProperties class and use them to create DataSource beans on-the-fly. I've tried using @PostConstruct and implementing BeanFactoryPostProcessor. However, with BeanFactoryPostProcessor, the processing seems to happen to early - before my ConfigurationProperties class has been populated. How can I read properties and create DataSource beans on the fly with Spring Boot?

这是我的 application.properties 的样子:

Here's what my application.properties looks like:

ds.clients[0]=client1|jdbc:db2://server/client1
ds.clients[1]=client2,client3|jdbc:db2://server/client2
ds.clients[2]=client4|jdbc:db2://server/client4
ds.clients[3]=client5|jdbc:db2://server/client5

还有我的 ConfigurationProperties 类:

And my ConfigurationProperties class:

@Component
@ConfigurationProperties(prefix = "ds")
public class DataSourceSettings {
    public static Map<String, String> CLIENT_DATASOURCES = new LinkedHashMap<>();

    private List<String> clients = new ArrayList<>();

    public List<String> getClients() {
        return clients;
    }

    public void setClients(List<String> clients) {
        this.clients = clients;
    }

    @PostConstruct
    public void configure() {
        for (String client : clients) {
            // extract client name
            String[] parts = client.split("\|");
            String clientName = parts[0];
            String url = parts[1];
            // client to datasource mapping
            String dsName = url.substring(url.lastIndexOf("/") + 1);
            if (clientName.contains(",")) {
                // multiple clients with same datasource
                String[] clientList = clientName.split(",");
                for (String c : clientList) {
                    CLIENT_DATASOURCES.put(c, dsName);
                }
            } else {
                CLIENT_DATASOURCES.put(clientName, dsName);
            }
        }
    }

在这个 @PostConstruct 方法的末尾,我想用这些设置创建一个 BasicDataSource 并将它添加到 ApplicationContext.但是,如果我尝试通过实现 BeanFactoryPostProcessor 和实现 postProcessBeanFactory 来做到这一点,clients 属性为 null,CLIENT_DATASOURCES 我用 @PostConstruct 填充.

At the end of this @PostConstruct method, I'd like to create a BasicDataSource with these settings and add it to the ApplicationContext. However, if I try to do this by implement BeanFactoryPostProcessor and implementing postProcessBeanFactory, the clients property is null, as is the CLIENT_DATASOURCES that I've populated with @PostConstruct.

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    System.out.println("clients: " + CLIENT_DATASOURCES);
}

使用 Spring Boot 即时创建数据源的最佳方法是什么?

What's the best way to create datasources on-the-fly with Spring Boot?

推荐答案

如何创建 bean 并要求 Spring Boot 向其中注入值?

How about creating your beans and ask Spring Boot to inject values into it?

类似的东西

@Bean
@ConfigurationProperties("ds.client1")
public DataSource dataSourceClient1() {
    DataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties("ds.client2")
public DataSource dataSourceClient2() {
    DataSourceBuilder.create().build();
}

然后,ds.client1 命名空间中的任何设置都属于第一个数据源(即 ds.client1.password 是该 的数据源密码数据源).

Then, any setting in the ds.client1 namespace belongs to the first data source (i.e. ds.client1.password is the data source password for that DataSource).

但也许您不知道您将拥有多少数据源?这变得越来越复杂,尤其是当您需要将这些动态数据源注入其他对象时.如果您只需要按名称查找它们,您可以将它们自己注册为单身人士.这是一个有效的例子

But maybe you don't know how much data sources you'll have? This is getting more complicated, especially if you need to inject those dynamic data sources in other objects. If you only need to lookup them by name, you could register them yourself as singletons. Here is an example that works

@ConfigurationProperties(prefix = "ds")
public class DataSourceSettings implements BeanFactoryAware {

    private List<String> clients = new ArrayList<>();

    private BeanFactory beanFactory;

    public List<String> getClients() {
        return clients;
    }

    public void setClients(List<String> clients) {
        this.clients = clients;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @PostConstruct
    public void configure() {
        Map<String, String> clientDataSources = new HashMap<String, String>();
        for (String client : clients) {
            // extract client name
            String[] parts = client.split("\|");
            String clientName = parts[0];
            String url = parts[1];
            // client to datasource mapping
            String dsName = url.substring(url.lastIndexOf("/") + 1);
            if (clientName.contains(",")) {
                // multiple clients with same datasource
                String[] clientList = clientName.split(",");
                for (String c : clientList) {
                    clientDataSources.put(c, url);
                }
            }
            else {
                 clientDataSources.put(clientName, url);
            }
        }
        Assert.state(beanFactory instanceof ConfigurableBeanFactory, "wrong bean factory type");
        ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
        for (Map.Entry<String, String> entry : clientDataSources.entrySet()) {
            DataSource dataSource = createDataSource(entry.getValue());
            configurableBeanFactory.registerSingleton(entry.getKey(), dataSource);
        }
    }

    private DataSource createDataSource(String url) {
        return DataSourceBuilder.create().url(url).build();
    }
}

请注意,这些 bean 可通过 bean 名称查找使用.如果这对您有用,请告诉我.

Note that those beans are only available by bean name lookup. Let me know if that works out for you.

这篇关于如何在 Spring Boot 中以编程方式创建 bean?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆