There are plenty of resources explaining how to configure data source in Spring Boot using various options and various ways to retrieve the configuration details of the connections (from external file, from application.properties, hard-coded, etc).
On the other hand, if you already have a data source configured and at runtime you want to change the details of this connection (perhaps hostname of database changes) without restarting the application, the information is a bit scarcer.

The use-case I am talking about is a situation where the user is allowed to change all or part of the connection details to the database (hostname for example).
The user has a Settings page where he can edit these details. This settings are kept in an external file, not in application.properties.
In this case, for the subsequent requests to that database, the new parameters need to be used.
The default behavior of Spring is to create Beans as Singleton, including the DataSource that you configure and thus, the bean won’t be recreated if you change the settings (it will still use the old parameters).

Here I present one of many approaches as to how this can be achieved.

Creating the data source

There are multiple ways to create a data source to use in Spring Boot and plenty of resources for this.
Here, I show how to create a simple JdbcTemplate based Repository configured in a separate package (will not use database properties defined in application.properties).

Here’s the configuration snippet for the data source:

@Configuration
public class CustomDataSourceConfiguration {

@Lazy
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DataSource customDataSource() {
DataSourceBuilder dsBuilder = DataSourceBuilder.create();
dsBuilder.driverClassName(„oracle.jdbc.driver.OracleDriver“);
CustomDatabaseSettings dbSettings = <….>//Here obtain the settings from whereever you need
dsBuilder.url(dbSettings.jdbcUrl());
dsBuilder.username(dbSettings.username());
dsBuilder.password(dbSettings.password());
return dsBuilder.build();
}

@Lazy
@Qualifier(„customJdbcTemplate“)
@Bean
public JdbcTemplate customJdbcTemplate() {
return new JdbcTemplate(customDataSource());
}

}

Here, using a class annotated as Configuration class, I define the Beans needed for the DataSource and for the JdbcTemplate.
The @Lazy annotation is important in this context since I read the external file which holds the database configuration from Java code and this happens after Spring Container initializes all beans.
By default, Beans are loaded Eagerly, meaning that when Spring scans for them and meets them, it will instantiate them. Lazy will wait with the initialization until the first request for that bean is made.
See the doc for @Lazy – https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Lazy.html

The Scope of the bean is set to ‚Prototype‘. By default the scope is set to Singleton, but as I said in the beginning of the article, we need a way to reinitialize the data source with new configuration details and the easiest way is using this scope. This scope will call the initialization each time the bean is requested.
As you can see from the code, this suits our case very well since calling again the method to initialize will also get the new configured database connection details.
There are some caveats to using Prototype.
More info about the scopes can be found in the docs: https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

Here’s the code the Repository as well:

@Lazy
@Repository
@ComponentScan(basePackages = „com.cli.jdbc.datasource“)
public class CustomRepository{

@Qualifier(„customJdbcTemplate“)
@Autowired
private JdbcTemplate jdbcTemplate;

//Implement methods here

So we’ve configured the DataSource by reading from our custom Configuration file which changes dynamically at runtime.
Now, when the Settings are changed, we need a way to reinitialize the data source.
Conceptually this is done by simply retrieving the DataSource bean from the Spring Context. Because we used Prototype scope, this will cause the logic in the above defined method to be called again which in turn will take the newest changes for our database. After this step, we need to retrieve the jdbcTemplate from the context as well and set this new DataSource to it.

The code looks like this:

public void refreshCustomJdbc() {
DataSource ds = (DataSource) getSpringContext().getBean(„customDataSource“);
JdbcTemplate customJdbcTemplate = (JdbcTemplate) getSpringContext().getBean(„customJdbcTemplate“);
customJdbcTemplate.setDataSource(ds);
}