Apache Commons logo Commons Configuration

Declaring and Creating Beans

Often it is good practice to make heavy use of Java interfaces and program an application or components against these interfaces rather than concrete implementation classes. This makes it possible to switch to different implementations without having to modify calling code. However the problem remains how a concrete implementation of an interface is obtained. Simply using the new operator on a specific implementation class would somehow break the interface concept because then the code would have an explicit reference to a concrete implementation.

A solution to this problem is to define the concrete implementation class that should be used in a configuration file. Client code would obtain an object (or a bean) from the configuration and cast it to the service interface. This way the caller would have no knowledge about which concrete implementation is used; it would only interact with the service through the interface. By changing the configuration file and entering a different class name for the implementation class the behavior of the application can be altered, e.g. to inject a test stub for the used service.

Note: The concept of defining service objects in configuration files and let them be created by a special container has grown popular these days. Especially IoC containers like HiveMind or Spring offer wide functionality related to this topic. Commons Configuration is not and has no ambitions to become an IoC container. The provided functionality for declaring and creating beans is very basic and limited compared to the specialists. So if you are in need of enhanced features like the creation of complete networks of service objects, life cycle handling and such things, you should in any case use a real IoC container. For simple use cases however the functionality of Commons Configuration might be sufficient, and we have tried to provide hooks for extending the predefined mechanisms.

Basic Concepts

Beans (we use the term bean here to name any plain old Java object that is defined in a configuration file and can be instantiated by Commons Configuration) are defined in configuration files in a specific format, a so called Bean declaration. Such a declaration contains all information needed to create an instance of this bean class, e.g. the full qualified name of the class and initialization parameters. We will explain how a bean declaration looks like in short.

On the Java side three entities are involved in the creation of a bean:

  • A bean factory: This is an object that implements the BeanFactory interface and knows how to create an instance of a bean class. In most cases calling code does not directly deal with a bean factory.
  • An implementation of the BeanDeclaration interface. This object knows how the bean declaration in the configuration file is organized and how the needed information can be extracted. So the way the bean is declared in the configuration file must match the expectations of this object.
  • The utility class BeanHelper brings all these together and performs the bean creation operation. Usually client code will create a BeanDeclaration object from a Configuration implementation and then pass it to one of the createBean() methods of BeanHelper. That's it!
For all of the interfaces mentioned above default implementations are provided, which in many cases can be used out of the box.

An Example

After this theory let's get into practice using an example. Consider a GUI application that makes use of a Window manager to display its windows and dialogs to the user. There is a WindowManager interface containing methods for opening, displaying, hiding, and disposing windows. Different implementations of this interface exist, e.g. providing different look & feel or special functionality. The concrete set of methods of the interface does not matter for this example.

Now in the application's configuration it shall be specified that the concrete implementation DefaultWindowManager should be used as WindowManager. This is a plain Java class implementing the WindowManager interface. Some fragments are shown in the following listing:

package examples.windows;

public class DefaultWindowManager implements WindowManager
{
    // are windows allowed to be resized?
    private boolean resizable;
    // do windows have a close button?
    private boolean closable;

    // Default size of new windows
    private int defaultWidth;
    private int defaultHeight;

    WindowStyleDefinition styleDefinition;

    // getters and setters ommitted, also the WindowManager methods
}

As you can see, the DefaultWindowManager class has some simple properties for defining the windows. There is also a property named StyleDefinition whose type is another bean (such a style definition may contain information about themes, colors, fonts of the window and so on). How can we now write a configuration file so that a bean of the DefaultWindowManager class is declared and initialization properties are defined? In an XML configuration file this will look as follows:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<config>
  <gui>
    <windowManager config-class="examples.windows.DefaultWindowManager"
      closable="false" resizable="true" defaultWidth="400"
      defaultHeight="250">
      <styleDefinition config-class="examples.windows.WindowStyleDefinition"
        backColor="#ffffff" foreColor="0080ff" iconName="myicon" />
    </windowManager>
  </gui>
</config>

This XML document contains a valid bean declaration starting with the windowManager element and including its sub elements. Note the following points:

  • The (full qualified) class of the bean is specified using the config-class attribute. (Attributes starting with the prefix "config-" are reserved; they contain special meta data for the bean creation process.)
  • Other attributes of the windowManager element correspond to properties of the DefaultWindowManager class. These properties will be initialized with the values specified here.
  • For the styleDefinition property, which is itself a bean, a sub element (matching the property's name) exists. The structure of this element is analogous to the structure of the windowManager element; indeed it could even have further sub elements defining bean properties of the WindowStyleDefinition class.
The basic structure of a bean declaration should have become clear by this example.

Now let's see how we can access this declaration and create an instance. This is demonstrated in the code fragment below:

XMLConfiguration config = new XMLConfiguration("windowconfig.xml");
BeanDeclaration decl = new XMLBeanDeclaration(config, "gui.windowManager");
WindowManager wm = (WindowManager) BeanHelper.createBean(decl);

This fragment loads the configuration file using a XMLConfiguration object. Then a bean declaration object is created, in this case an instance of the XMLBeanDeclaration class, which can deal with bean declarations in XML documents. This declaration is passed to the static createBean() method of the BeanHelper class, which returns the new bean instance.

BeanHelper defines some overloaded versions of the createBean() method. Some allow to pass in a default bean class; then it is not necessary to define the class in the bean declaration - an instance of this default class will be created if it is lacking in the configuration file. If the bean cannot be created for some reason (e.g. a wrong class name was specified), a ConfigurationRuntimeException will be thrown.

Extending the Basic Mechanism

As was pointed out in the introduction of this chapter support for creating beans is focused on the basics. But there are some possibilities of hooking in and add custom extensions. This can be done in the following ways:

  • By defining a custom BeanDeclaration implementation
  • By providing a custom BeanFactory implementation

A specialized bean declaration is needed when you have to deal with configuration files that contain bean declarations in a different format than the ones supported by the available default implementations. Then it is the responsibility of your implementation to parse the configuration data and extract the required information to create the bean. Basically your BeanDeclaration implementation must be able to provide the following data:

  • The name of the class for which an instance is to be created.
  • The name of the bean factory that is used to create the bean. Here null can be returned, then a default factory is used. (See below for more information about working with custom bean factories.)
  • An optional parameter to be passed to the bean factory. If a factory is used that supports additional parameters, the current parameter values are also obtained from the bean declaration.
  • A map with the properties to be set on the newly created bean. This map's keys are names of properties, its values are the corresponding property values. The default bean factory will process this map and call the corresponding setter methods on the newly created bean object.
  • A map with further BeanDeclaration objects for initializing properties of the new bean that are itself beans. These bean declarations are treated exactly as the one that is currently processed. The resulting beans will then be set as properties on the processed bean (the names of these properties are again obtained from the keys of the map).

While creating a custom BeanDeclaration implementation allows you to adapt the format of bean declarations in configuration files, you can manipulate the bean creation mechanism itself by creating a specialized implementation of the BeanFactory interface. For this purpose the following steps are necessary:

  1. Create a class implementing the BeanFactory interface. This interface is quite simple. It defines one method for creating an instance of a class whose Class object is provided, and another method, which is called for querying a default class.
  2. Register this new factory class at the BeanHelper class.
  3. In the bean declaration in your configuration file refer to the factory that should be used for creating the bean.

We will provide an example that covers all these steps. This example deals with a singleton factory, i.e. an implementation of BeanFactory that returns always the same instance of a provided bean class.

We start with the creation of the factory class. The basic idea is that the functionality for creating and initializing beans is already provided by the DefaultBeanFactory class, so we extend this class. Our implementation only has to deal with the singleton stuff: We keep a map that stores already created bean instances and can be accessed by the name of their classes. In the factory's createBean() method we check if for the passed in class already an instance exists. If this is the case, it is directly returned. Otherwise we call the inherited createBean() method and store its result in the map. (Note that this implementation is a bit simplistic; a real world implementation would also have to take the initialization parameters into account. But for the purpose of an example it should be good enough). Here is the code:

public class SingletonBeanFactory extends DefaultBeanFactory
{
    /** A map for the so far created instances.*/
    private Map beans;

    public SingletonBeanFactory()
    {
        super();
        beans = new HashMap();
    }

    // Creates the bean. Checks if already an instance exists.
    public synchronized Object createBean(Class beanClass, BeanDeclaration decl,
        Object param) throws Exception
    {
        Object bean = beans.get(beanClass.getName());
        if (bean != null)
        {
            // Yes, there is already an instance
            return bean;
        }
        else
        {
            // No, create it now (done by the super class)
            bean = super.createBean(beanClass, decl, param);
            // Store it in map
            beans.put(beanClass.getName(), bean);
            return bean;
        }
    }
}

Note the synchronized key word, which is necessary because the method can be accessed by multiple threads concurrently. Now we have to register an instance of this class at the BeanHelper class. This can be done in the initialization phase of your application and looks as follows:

BeanHelper.registerBeanFactory("SINGLETON", new SingletonBeanFactory());

To make use of the new factory a bean declaration must contain an attribute that refers to the name under which the factory was registered. This is demonstrated by the fragment below:

<config>
...
    <services>
      <fileService config-class="my.package.services.FileServiceImpl"
        config-factory="SINGLETON"
        property1="value1" property2="value2">
        <!-- Here can be nested bean declarations -->
      </fileService>
      ...
</config>

In this fragment the fileService element contains a bean declaration for some service object. Apart from the config-class attribute the important part is the config-factory attribute. This attribute tells the BeanHelper class that it should use a special factory when it processes this bean declaration. As was demonstrated by this example, it should not be too difficult to extend the custom mechanism for creating beans.