Choose many Spring contexts over a single context

This is a short introduction to splitting up your bean definitions from one single Spring application context, to many application contexts. That is, from one XML file to many XML files. The general idea is to have one single primary application context, usually titled applicationContext.xml, and then many other application contexts with are named what they contain.

Example

For example, a single web project might contain the following contexts.

Breakdown

In this example, your DAO definitions, transaction managers, and DAO or integration interceptors are defined in applicationContext-dao.xml. We have further broken the DAO application context to a dao-datasource which contains our data sources. LDAP configuration, beans, and DAOs are in their own applicationContext-ldap.xml.

Benefits

Why so many? There have been a few benefits so far.

Responsibility

The Single Responsibility Principal is widely accepted and states that

… every object should have a single responsibility, and that all its services should be narrowly aligned with that responsibility.states that every object should have a single responsibility, and that all its services should be narrowly aligned with that responsibility.

The same holds true for XML contexts.  The general ides is to make our contexts more robust and to be able to isolate a context’s tasks and capabilities. And clearly, working with a smaller XML file is easier than working with one enormous file. It is easier to maintain and easier to read.

Testing

When I wrote my tests for LDAP, I had my LDAP configuration in applicationContext.xml, but then every time I ran an LDAP test, I would get the WebApplicationContext, which would import applicationContext-hibernate, which would connect to the database. Tests took forever and needless resources were used. Now I just get applicationContext-ldap.xml which only contains LDAP configurations, so no needless resources are loaded and tests remain quick.

Inclusion and Exclusion

Finally, its really easy to comment out a line like

<import resource="applicationContext-aspects.xml"/>

and quickly have all my aspects, which for me only write logs, disabled. Or easy to add a line like

<import resource="applicationContext-dao-mock.xml"/>

which overrides all of your DAO implementations with mock DAOs. This would be for testing.

Does anyone else use multiple application context files? See any other benefits? Or problems?

Share

PropertyPlaceholderConfigurer with Default Values

My current project relies heavily on Spring. We use the PropertyPlaceholderConfigurer so that our application contexts can pull values from the properties files and inject them into our beans. This is all very common. This means when the beans are being created and a value like ${someproperty} shows up, the BeanFactory visits the configured properties files to find the value for someproperty and injects that value into the bean.

The problem arises when a bean is configured using such a placeholder but the requested property is not found. This causes our application to fail to start.

Our solution was to extend PropertyPlaceholderConfigurer to provide default values. These default values are loaded before loading any properties files.  Firs well glance over the java class.

import java.io.IOException;
import java.util.Properties;
import java.util.Map;
import java.util.HashMap;

public class DefaultPropertyPlaceholderConfigurer
        extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
{
  private Map<String, String> startingProperties = new HashMap<String, String>();

  public void setStartingProperties(Map<String, String> startingProperties)
  {
     this.startingProperties = startingProperties;
  }

  public DefaultPropertyPlaceholderConfigurer()
  {
      try
      {
          loadDefaultProperties();
      }
      catch (IOException e)
      {
          logger.warn("failed to load default properties", e);
      }
  }
   private void loadDefaultProperties()
          throws IOException
  {
      final Properties defaultProperties = new Properties();
       for(Map.Entry<String,String> entry : startingProperties.entrySet())
      {
          defaultProperties.put(entry.getKey(), entry.getValue());
      }
       this.setPropertiesArray(new Properties[]{defaultProperties});
  }
}

And the XML bean definition for our new PropertyPlaceholderConfigurer.

  <bean id="propertyConfigurer"
        class="app.factory.config.DefaultPropertyPlaceholderConfigurer">
      <property name="ignoreUnresolvablePlaceholders" value="true" />
      <property name="startingProperties">
          <map>
              <entry key="one.two.three" value="123" />
          </map>
      </property>
      <property name="locations">
          <list>
              <value>/WEB-INF/classes/preferences.properties</value>
          </list>
      </property>
  </bean>

So now, when one.two.three is not included in the properties file preferences.properties the value will be initialized to 123. The value is defined in the properties file, the default value of 123 is overridden with the custom value.

Use Case

Here are the details on our specific use case. We release updates that add new properties, however, in order for the user to run the updates, the application has to start first (it is a web application). If the property is not set then the BeanFactory fails to start the application because it can not resolve the placeholder. So the software can’t start without running the updates first, but the updates can not be run until the application is started.

Now the web application starts and uses the default value, the administrator can run the updates which add the custom values to the preferences.properties file, and the user is prompted to restart the software after the updates are complete. The restart is required to read the new values into the bean.

Oops

While I was writing this post I realized that there was a much simpler way to provide default properties without extending the base class.

Any ideas as to how I could have done it?

Share

← Previous Page