Declaring and Creating BeansOften 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 ConceptsBeans (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:
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
Now in the application's configuration it shall be specified that the
concrete implementation 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 <?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
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
Extending the Basic MechanismAs 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:
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
While creating a custom
We will provide an example that covers all these steps. This example
deals with a singleton factory, i.e. an implementation of
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 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.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 |