Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Custom Property in Spring

DZone's Guide to

A Custom Property in Spring

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

<context:property-placeholder> is a really easy way to provide property replacements in Spring configurations with values from a standard Java Properties file. But what if you don’t want a property hard coded into a file – a clear text password for instance? Spring provides all the bits and pieces to write your own property replacement. Let me introduce my CustomPropertyConfigurer.

I’ll demonstrate using a variation on the theme of the Spring JDBC Template. MyQuery is a simple extension of org.springframework.jdbc.core.JDBCTemplate that gets the current timestamp from a MySQL database. Here’s the, hopefully familiar, configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
  <context:property-placeholder location="classpath:jdbc.properties" />
 
  <bean id="myQuery" class="rob.MyQuery">
    <property name="dataSource" ref="dataSource" />
  </bean>
 
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
  </bean>
 
</beans>

Except the jdbc.properties file does not contain the password:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://rob-7
jdbc.username=rob

I will set the jdbc.password property myself from what is entered on the command line.

public static void main(String... args) {
 
    char[] password = System.console().readPassword("Password: ");
 
    Properties properties = new Properties();
    properties.setProperty("jdbc.password", new String(password));
 
    ConfigurableApplicationContext context =
        new ClassPathXmlApplicationContext(
            new String[] {
                "rob/MyQuery.xml"},
            false);
 
    context.addBeanFactoryPostProcessor(
            new CustomPropertyConfigurer(properties));
    context.refresh();
 
    MyQuery myQuery = context.getBean(MyQuery.class);
 
    myQuery.run();
 
    context.close();
}

Where the CustomPropertyConfigurer is:

import java.util.Properties;
 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.StringValueResolver;
 
public class CustomPropertyConfigurer implements BeanFactoryPostProcessor {
 
    private final Properties properties;
 
    public CustomPropertyConfigurer(Properties properties) {
        this.properties = properties;
    }
 
    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactoryToProcess)
    throws BeansException {
 
        BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(
                new BeanDirectoryResolver());
 
        String[] beanNames =
                beanFactoryToProcess.getBeanDefinitionNames();
        for (int i = 0; i < beanNames.length; i++) {
 
            BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(
                    beanNames[i]);
            try {
                visitor.visitBeanDefinition(bd);
            }
            catch (BeanDefinitionStoreException ex) {
                throw new BeanDefinitionStoreException(
                        bd.getResourceDescription(), beanNames[i],
                        ex.getMessage());
            }
        }
    }
 
    class BeanDirectoryResolver implements StringValueResolver {
 
        private final PropertyPlaceholderHelper helper;
 
        public BeanDirectoryResolver() {
            helper = new PropertyPlaceholderHelper("${", "}");
        }
 
        public String resolveStringValue(String strVal) {
            return helper.replacePlaceholders(strVal, properties);
        }
    }
}

The CustomPropertyConfigurer gets applied first. It leaves any properties it can’t resolve (all but the password) for the standard <context:property-configurer> to resolve. Unit tests running against a different jdbc.propeties file can continue to provide the password as before.

Here it is running:

Cusom Propery Configurer Running

There are many other examples of configuration values that might only be discovered at runtime – file names, schedule dates, form values etc. So long as the value can be a String, a CustomPropertyConfigurer provides a simple way of passing these values to Spring.

 

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}