Allen Chee

About Allen Chee

Allen is a software developer working in the banking domain. Apart from hacking code and tinkering with technology, he reads a lot about history, so that mistakes of the past need not be repeated if they are remembered.

Spring 3.1 – Loading Properties For XML Configuration From Database

Spring makes it easy to inject values obtained from properties files via its PropertyPlaceholderConfigurer and (pre-Spring 3.1) PropertySourcesPlaceholderConfigurer (Spring 3.1). These classes implement the BeanFactoryPostProcessor interface, which enables them to manipulate the values within the Spring XML configuration file before the beans are initialized. So if you specify ${jdbc.driverClassName} to be set to the property ‘driverClassName’, this variable will be replaced/swapped with the value with the key ‘jdbc.driverClassName’ in a properties file.

Apart from properties files, the database table can also be a place to get key-value pairs. Great, so just extend the PropertySourcesPlaceholderConfigurer, and have it read a table containing the key-value pairs, populate them and we’re done!

However, there’s a slight problem. If the DataSource bean also relies on values obtained from a properties file (e.g. JDBC URL, username, password), and being good Springers, inject this bean to the bean class extending PropertySourcesPlaceholderConfigurer, the bean container will fail to startup properly, because the ‘jdbc.driverClassName’ variable cannot be resolved. Strange, but true.

The reason for this is that any bean injected into a BeanFactoryPostProcessor class will trigger bean initialization BEFORE the BeanFactoryPostProcessor classes are run. You know, dependency injection…all depending beans have to be ready before being injected into the consumer. So this creates a cyclic-dependency kind of thing. All dependencies in the XML configuration are resolved first before the BeanFactoryPostProcessor classes are run.

So, how to go about this? Well, there’s a trick you can employ. A BeanFactoryPostProcessor class has access to the ConfigurableListableBeanFactory object via the ‘postProcessBeanFactory’ method. From this object, you can do a ‘getBean’ and get a reference of any bean with an id. And guess what, you can get the vaunted DataSource bean without triggering premature bean initialization.

Let’s say there’s a table ‘sys_param’ with the following data:

PARAM_CD PARAM_VALUE
-------------- --------------
service.charge 1.5
rebate.amount 15.99
smtp.ip 173.194.79.16

The DbPropertySourcesPlaceholderConfigurer is shown here:

package org.gizmo.labs.utils.spring;

import javax.sql.DataSource;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

public class DbPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer
{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
	{
		DataSource dataSource = beanFactory.getBean(DataSource.class);
		DbProperties dbProps = new DbProperties(dataSource);

		setProperties(dbProps);
		super.postProcessBeanFactory(beanFactory);
	}
}

The DbProperties class will make use of the DataSource reference and queries the database to get the key-value pairs:

package org.gizmo.labs.utils.spring;

import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

public class DbProperties extends Properties
{
	private final Logger logger = LoggerFactory.getLogger(DbProperties.class);
	private static final long serialVersionUID = 1L;

	public DbProperties(DataSource dataSource)
	{
		super();
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 
		List
                    <map
                     > l = jdbcTemplate.queryForList('select param_cd, param_value from sys_param');

		for(Map

                       m: l)
		{
			logger.debug('Loading from DB: [{}:{}]', m.get('PARAM_CD'), m.get('PARAM_VALUE'));
			setProperty((m.get('PARAM_CD')).toString(), (m.get('PARAM_VALUE')).toString());
		}
	}
}

To demonstrate that the values from the table are properly injected, here’s the class which acts as the consumer:

package org.gizmo.labs.utils.spring;

import java.math.BigDecimal;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

public class DbPropConsumer implements InitializingBean
{
	private final Logger logger = LoggerFactory.getLogger(DbPropConsumer.class);

	private BigDecimal serviceCharge;
	private double rebateAmount;
	private String smtpIp;

	@Override
	public void afterPropertiesSet() throws Exception
	{
		logger.debug('I have consumed: {}', this);
	}

	public String toString()
	{
		return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
	}	

	public BigDecimal getServiceCharge() {
		return serviceCharge;
	}

	public void setServiceCharge(BigDecimal serviceCharge) {
		this.serviceCharge = serviceCharge;
	}

	public double getRebateAmount() {
		return rebateAmount;
	}

	public void setRebateAmount(double rebateAmount) {
		this.rebateAmount = rebateAmount;
	}

	public String getSmtpIp() {
		return smtpIp;
	}

	public void setSmtpIp(String smtpIp) {
		this.smtpIp = smtpIp;
	}

}

Last but not least, the Spring configuration (DataSource bean not shown, simplified for clarity):

classpath:system.properties

The first 2 bean definitions are the BeanFactoryPostProcessor classes, and to ensure the first one is run first, the ‘order’ property is set (lower means higher precedence).

For the DbPropertySourcesPlaceholderConfigurer, a different placeholder prefix and suffix is used for clarity (notice the placeholders for DbPropConsumer).

So, upon Spring container startup, you should be able to view a similar output as below:

2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbProperties, Loading from DB: [service.charge:1.5]
2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbProperties, Loading from DB: [rebate.amount:15.99]
2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbProperties, Loading from DB: [smtp.ip:173.194.79.16]
2012-09-18 00:03:14, DEBUG, org.gizmo.labs.utils.spring.DbPropConsumer, I have consumed: org.gizmo.labs.utils.spring.DbPropConsumer@189b939[
logger=Logger[org.gizmo.labs.utils.spring.DbPropConsumer]
serviceCharge=1.5
rebateAmount=15.99
smtpIp=173.194.79.16
]

 

Reference: Spring 3.1 – Loading Properties For XML Configuration From Database from our JCG partner Allen Julia at the YK’s Workshop blog.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


− 2 = three



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close