MemeStorm

Exploring the Spring Framework and Application Development

Parameterizing Spring’s Configuration Files, and BeanFactoryPostProcessors

Posted in All by Jon on the February 13th, 2006

You have a few Spring context configuration files, say dispatch-servlet.xml as in the Basic Spring Web MVC Example, and you want to avoid hardcoding property values. Instead of embedding them in the XML files, you want to place them in a simple property file, making life much easier for folk who use and deploy your application. In what follows, we’ll show you how to do this, and explain how it works by diving into the IoC container.

How to do it

Say for example you are configuring a data source bean. You know that its configuration is dependent on a JDBC driver’s classname, and you know that this classname will differ, depending on what your client uses. So it makes sense to put it in an external property file. So, for the value of the property, insert a property placeholder instead:


      class=”org.springframework.jdbc.datasource.DriverManagerDataSource”>

Now create a property file that defines the value, for example here is our jdbc.properties file:

jdbc.driverClassName=com.memestorm.JDBCDriver
other.properies=othervalues

Now the only thing left is to somehow tell the context configuration file to substitute the property placeholders with the values defined in the property file. You do that by adding the following to your context configuration file (for example dispatch-servlet.xml):


      class=”org.springframework.beans.factory.config.PropertyPlaceholderConfigurer”>

That’s it. It doesn’t matter if you put this before or after the definition of the dataSource bean for example, it will still work. Spring will magically read the configuration file and substitue the values in the beans that you define.

How it works

We’re not going to explain how the
PropertyPlaceholderConfigurer reads the configuration
file, parses out the properties, and applies them. Instead, we’re
going to tell you how it gets activated at all and how it gets access to
your beans.

The key is that PropertyPlaceholderConfigurer
implements a special interface,
BeanFactoryPostProcessor,
which has a single method:

void postProcessBeanFactory(
              ConfigurableListableBeanFactory beanFactory)
              throws BeansException;

The next key is that any application context (such as the servlet web
application context, or a root application context that you may
define), after it has read the configuration, looks through all the beans for any that
implement the BeanFactoryPostProcessor interface. That’s
right, it automatically checks all the beans that are defined, looking
for those that implement BeanFactoryPostProcessor. It
then invokes the postProcessBeanFactory() method on those
beans. [Aside:This kind of processing doesn’t happen in a BeanFactory - this is what distinguishes a BeanFactory from a Context]

You should now have an idea of what happens: Spring’s core IoC
system (let’s call it AbstractApplicationContext) loads
the context configuration file, it searches for beans implementing
BeanFactoryPostProcessor and will find our property
configurer, as PropertyPlaceholderConfigurer implements
this interface. The application context then runs the postProcessBeanFactory() method in all appropriate beans; in our case this means that thePropertyPlaceholderConfigurer bean (well, actually a
subclass but no matter) then loads the properties from the configured
location. Since this method gets passed the list of parsed beans in the configuration file (see the beanFactory parameter), it can then operate on them, substituting the place holders with the appropriate values. After the AbstractApplicationContext has run all the BeanFactoryPostProcessors, it continues with its job of setting up the context. Here’s a snippet from the internals:

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors();
// Register bean processors that intercept bean creation.
registerBeanPostProcessors();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate singletons this late to allow them to access the message source.
beanFactory.preInstantiateSingletons();
// Last step: publish corresponding event.
publishEvent(new ContextRefreshedEvent(this));

As you can see, the bean factory processors are run first, before the rest of the magic happens (including instantiating singleton beans).

Neat hey!

Advanced

You can write your own bean factory processors quite easily. Perhaps, as suggested in Spring’s documentation, you can use it to unencrypt/encrypt some bean values. Simply make sure it implements the appropriate interface and it will then run as explained before. (In fact, you can even apply some order to them). Here’s a simple example. Just reference this bean from your context:

package com.memestorm;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyPostProcessor implements BeanFactoryPostProcessor {

  Log logger = LogFactory.getLog(getClass());

  public void postProcessBeanFactory(
	ConfigurableListableBeanFactory beanFactory) throws BeansException {
    logger.info("In postProcessBeanFactory.  The following beans are available:");
    String [] n = beanFactory.getBeanDefinitionNames();
    for (int i=0; i < n.length; i++) {
      logger.info("Bean = " + n[i]);
    }
  }
}

Print This Post Print This Post

3 Responses to 'Parameterizing Spring’s Configuration Files, and BeanFactoryPostProcessors'

Subscribe to comments with RSS or TrackBack to 'Parameterizing Spring’s Configuration Files, and BeanFactoryPostProcessors'.

  1. Lou Mauget said,

    on February 21st, 2007 at 7:36 am

    Good explation of how it works, This would be more complete with applicationContext.xml and properties file snippets.


  2. on November 22nd, 2007 at 11:47 am

    […] note we’ve externalized (see Parameterizing Spring’s Configuration Files, and BeanFactoryPostProcessors) the name of the database driver and URL used for connecting to the database. You’ll find […]

  3. codeman said,

    on December 20th, 2007 at 6:31 pm

    Thanks for such a clear explanation !

Leave a Reply