pure utils:property, one location
With the utils namespace in Spring 3.0 you can load a property file and use the values in Spring EL expressions.
— Spring config File —
... <util:properties id="propsWitUtils" location="classpath:mybasic.properties"/> <bean id="someProps" class="ch.nydi.spring.config.SystemPropertiesFactoryBean"> <property name="propagateToSystemProperties" value="true" /> <property name="locations"> <list> <value>classpath:mybasic.properties</value> <value>classpath:myspecific.properties</value> </list> </property> </bean> <bean class="ch.nydi.spring.config.ConfigValueBean"> <property name="welcome" value="#{propsWitUtils['msg.welcome']}"/> <property name="dbUser" value="#{someProps['database.user']}"/> <property name="dbPassword" value="#{someProps['database.password']}"/> </bean> ....
To use the property values in Java classes use the the EL expression with the @Value annotation.
— Java code —
... String @Value("#{someProps['my.tmp.dir']}") tmpDir; ...
Multiple property files
The utils namespace allows one location, unlike the valued flexibilty of other Spring components, it lacks possibilty to add multiple property file locations. But there is no problem to support multiple property locations by usage the source of util:properties.
In the xsd of the utils namespace you will find that utils:properties is based on org.springframework.beans.factory.config.PropertiesFactoryBean. PropertiesFactoryBean allows mutiple locations. With drect use of PropertiesFactoryBean a list of locations is allowed and all features you have with util:properties are still supported.
— Spring config File —
classpath:mybasic.properties classpath:myspecific.properties
The property values are still avaiable via EL expressions.
If the same property key is defined in mybasic.properties and myspecific.properties the value of myspecific.properties is taken. PropertiesFactoryBean allows to define properties in the bean definition with or . This values can also overriden by same keys defined in the file location if is set.
Use system properties and propagate properties to sytem properties
For example we have two property files
— mybasic.properties —
database.user=nydi
database.password=youwillneverhack
my.tmp.dir=${user.home}/tmp/public
— myspecific.properties —
my.tmp.dir=${user.home}/tmp/backoffice
The value of my.tmp.dir depends on application type where myspecific.properties is come from (contained in classpath) and ${user.home} has to be replaced with the value of system properties.
To implement that we slightly extend PropertiesFactoryBean and write a PropertyVariableParser which replaces the system property values parsed from the property values.
— Java code —
public class SystemPropertiesFactoryBean extends PropertiesFactoryBean { private boolean propagateToSystemProperties = false; public void setPropagateToSystemProperties(final boolean propagateToSystemProperties) { this.propagateToSystemProperties = propagateToSystemProperties; } @Override protected Properties createProperties() throws IOException { final Properties originalProperties = super.createProperties(); final Properties propertiesReplaced = new Properties(); final PropertyVariableParser propertyParser = new PropertyVariableParser(); for (final Map.Entry entry : originalProperties.entrySet()) { propertiesReplaced.put( entry.getKey(), propertyParser.replaceVariables((String) entry.getValue()) ); } if (propagateToSystemProperties) { final Properties systemProperties = System.getProperties(); systemProperties.putAll(propertiesReplaced); System.setProperties(systemProperties); } return propertiesReplaced; } }
and the PropertyVariableParser
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PropertyVariableParser { private final Logger log = LoggerFactory.getLogger(PropertyVariableParser.class); /** * Replaces variables with the pattern ${aSystemPropertyValue}. * * @param propertyValue * a string with the pattern ${asystem.property.value} * @return replacement of propertyValue with the value of System.getEnv(propertyValue); */ public String replaceVariables(String propertyValue) { final Scanner scanner = new Scanner(propertyValue); scanner.useDelimiter("${"); while (scanner.hasNext()) { String propertyName = scanner.next(); final int endindex = propertyName.indexOf('}'); if (-1 != endindex) { propertyName = propertyName.substring(0, endindex); } else { continue; } final String systemPropertyValue = System.getProperty(propertyName); if (null == systemPropertyValue) { log.warn("no such system property: " + propertyName); } else { propertyValue = propertyValue.replace("${" + propertyName + "}", systemPropertyValue); } } return propertyValue; } }
In the Spring config you have to define the SystemPropertiesFactoryBean instead of PropertiesFactoryBean.
— Spring config File —
classpath:mybasic.properties classpath:myspecific.properties
If the classpath contains more than one myspecific.properties file and you will respect all, the define an asterisk after classpath. classpath*:myspecific.properties. But consider, the order of wich one is read first and last is often confusion.
Visit spring-practice project at my github account to download the code.