Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically create spring beans and alter properties on existing beans

Tags:

java

spring

I sucessfully managed to implement dynamic changing of database connections by following http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/ article.

But now the problem is, I have a list of database urls in a configuration file that is managed by a legacy application.

Is there a way to create beans in that Spring context from a list of values (i.e. Year2011DataSource, Year2012DataSource,...) and populate map of the dataSource bean with those beans that were just created?

<!-- Property file located in the legacy application's folder -->
<context:property-placeholder location="file:///D:/config.properties" />

<!-- Shared data source properties are read from the config.properties file -->
<bean id="parentDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" abstract="true">
    <property name="driverClassName" value="${db.driver}" />
    <property name="username" value="${db.user}" />
    <property name="password" value="${db.password}" />
</bean>

<!-- Database urls by year -->
<bean id="Year2012DataSource" parent="parentDataSource">
    <property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2012" />
</bean>
<bean id="Year2011DataSource" parent="parentDataSource">
    <property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2011" />
</bean>
<bean id="Year2010DataSource" parent="parentDataSource">
    <property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2010" />
</bean>
<!-- ... and so on, these should instead be populated dynamically ... -->

<!-- DbConnectionRoutingDataSource extends AbstractRoutingDataSource -->
<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
    <property name="targetDataSources">
        <map key-type="int">
            <entry key="2011" value-ref="Year2011DataSource" />
            <entry key="2010" value-ref="Year2010DataSource" />
            <!-- ... and so on, these also should instead be populated dynamically ... -->
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="Year2012DataSource" />
</bean>
like image 228
Vedran Avatar asked Aug 23 '12 11:08

Vedran


People also ask

How do you make conditionally beans?

To conditionally create a bean, we must first create an implementation of Condition. The Condition interface contains the matches method which returns a boolean value. Here, the AuditEnabledCondition class is checking whether audit. enabled is true using the Environment properties.

How do I change the application properties value at runtime Spring boot?

To change properties in a file during runtime, we should place that file somewhere outside the jar. Then we tell Spring where it is with the command-line parameter –spring. config. location=file://{path to file}.


2 Answers

A good fit for this requirement I think is a custom BeanFactoryPostProcessor - read in the legacy configuration and generate the datasources in the custom bean factory post processor:

class MyDatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //Read in details from legacy properties.. build custom bean definitions and register with bean factory
        //for each legacy property...
            BeanDefinitionBuilder datasourceDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicDataSource.class).addPropertyValue("url", "jdbc..");
            beanFactory.registerBeanDefinition(datasourceDefinitionBuilder.getBeanDefinition());
    }
}
like image 112
Biju Kunjummen Avatar answered Sep 25 '22 06:09

Biju Kunjummen


As far as I know, there is no out-of-the-box solution using XML configuration. However, a simple solution to achieve this is described in this answer using FactoryBean abstraction in Spring.

like image 23
nobeh Avatar answered Sep 24 '22 06:09

nobeh