java - 如何在 Spring Boot 中以编程方式创建 bean?

标签 java spring spring-boot

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

这是我的 application.properties 的样子:

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 类:

@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 填充。

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

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

最佳答案

创建 bean 并让 Spring Boot 向其中注入(inject)值怎么样?

类似

@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 是该 的数据源密码数据源)。

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

@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 名称查找获得。让我知道这是否适合你。

关于java - 如何在 Spring Boot 中以编程方式创建 bean?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25160221/

相关文章:

java - Spring Boot JPA Many2One、One2Many两种方式无法正常工作

使用带有 Junit 的 Spring Boot 的 Wiremock 进行 Java 集成测试时出现 java.net.SocketTimeoutException

java - 带有 LookAhead 的 Lucene 分词器

java - 检查 Android 模拟器是否处于静音铃声模式

java - 内存映射类路径资源

java - Spring查询字符串简单验证?

java - 执行更新耗尽结果

java - Spring MVC - 没有绑定(bind)到线程的 Hibernate session

java - 无法使用 Spring javamail 在电子邮件中发送图像

java - 如何从 spring-boot 执行 postgres sql block