简体   繁体   中英

Spring Boot 2 Instanciating Bean from Configuration List

Hy all,

in my current application, I need to be able to load a number of database connections into my context (they are then used in camel routes, but thats not part of this question).
For this I have a List of settings in my application.yml file, where each entry is all that is needed for one connection:

    - name: mysql1
      jdbcUrl: "jdbc:mysql://localhost:3306/dbScheme"
      username: a
      password: a
    - name: mssql1
      jdbcUrl: "jdbc:sqlserver://localhost:1433;databaseName=dbScheme"
      username: a
      password: a

There is a variable amount of connections, that can be configured this way.
I need each of those configurations as a javax.sql.DataSource object/bean in the registy (bean name would be the name property from the configuration object):

DataSource dataSourceBean = DataSourceBuilder.create()
    .driverClassName(driverClassName) // Resolved automatically from the jdbcUrl
    .url(dataSourceFromYaml.getJdbcUrl())
    .username(dataSourceFromYaml.getUsername())
    .password(dataSourceFromYaml.getPassword())
    .build();

The question now is, how I do put those objects into the context as beans?
As I see it, its not possible with annotations, because we have a variable amount of objects and they are also not loaded into the context directly, but have to be created first. (Or am I wrong here?)

Thanks in advance for any ideas
Chris

Edit:
To clarify: The two connections I put here are not the connections used in production, they are just an example.
The issue is, that they are configured in production and there can be any amount of them.
I have no way of predicting how many there are and how they are named.

You could use the following approach

  1. create properties file application-dev.properties

     mysql1.jdbc.jdbcUrl=jdbc:mysql://localhost:3306/dbScheme mysql1.jdbc.username=a mysql1.jdbc.password=a mssql1.jdbc.jdbcUrl=jdbc:sqlserver://localhost:1433;databaseName=dbScheme mssql1.jdbc.username=a mssql1.jdbc.password=a
  2. Create configuration class

     @Configuration public class JDBCConfigExample { @Bean(name = "mysql1DataSource") @ConfigurationProperties(prefix = "mysql1.jdbc") public DataSource mysql1DataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "mssql1DataSource") @ConfigurationProperties(prefix = "mssql1.jdbc") public DataSource mssql1DataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "mysql1JdbcTemplate") public JdbcTemplate mysql1JdbcTemplate(@Qualifier("mysql1DataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean(name = "mssql1JdbcTemplate") public JdbcTemplate mssql1JdbcTemplate(@Qualifier("mssql1DataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } }
  3. After that you could use both JdbcTemplate and DataSource in any Spring managed bean by using Qualifier or name convention:

     private final JdbcTemplate mssql1JdbcTemplate;

If you don't have list of datasources beforehand and want to configure this dynamically you can do following thing:

1) In your application.yml:

    datasources:
      ds1:
        name:
        pw: 
      ds2:
        name: 
        pw: 

2) Create properties class:

@Configuration
@ConfigurationProperties(prefix = "datasources")
public class DataSourcesInfo {

    Map<String, DataSourceInfo> dataSources;

    public @Data static class DataSourceInfo {
        private String pw;
        private String name;
    }
}

In this map you will have list of entries for all datasources defined.

3) And now you can create beans dynamically:

    @Configuration
    public class Config {
        @Autowired
        private GenericApplicationContext genericApplicationContext;

        @Autowired
        DataSourcesInfo properties;

        @PostConstruct
        public void createDataSources() {
            // iterate through properties and register beans
            genericApplicationContext.registerBean(dataSourceName, 
            DataSource.class,
            () -> {
              DataSource ds = new DataSource();
              ds.set(...);
              ....
              return ds;
            })
        }
    }

After trying a lot of different spring context objects I finally found one, that worked.
Here is the solution:

@Configuration
@ConfigurationProperties(prefix = "jdbc")
@RequiredArgsConstructor // Lombok
public class DataSourceBeanFactory {

  // The DataSourceConfiguration holds everything from one property object
  @Setter // Lombok
  private List<DataSourceConfiguration> dataSources;
  private final ConfigurableApplicationContext applicationContext;

  @PostConstruct
  public void resolveAndCreateDataSourceBeans() {
    dataSources.forEach(dataSourceFromYaml -> {
      /*
       * Code to resolve driver class name
       */
      String driverClassName = ....

      DataSource dataSourceBean = DataSourceBuilder.create()
          .driverClassName(driverClassName)
          .url(dataSourceFromYaml.getJdbcUrl())
          .username(dataSourceFromYaml.getUsername())
          .password(dataSourceFromYaml.getPassword())
          .build();

      applicationContext
          .getBeanFactory()
          .registerSingleton(dataSourceFromYaml.getName(), dataSourceBean);
    });
  }

Thank to everybody, that answered my question.
Chris

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM