Author: Timo Laitinen
Date: 2005-06-30
Contents
1. About this document
Spring framework is an advanced and popular J2EE Application Framework that provides, among other features, also an XML-based application configuration solution. This document analyzes the similarities and differences between JICE and Spring Framework and studies how these two technologies could be used together.
The first part of this document will compare the configuration capabilities of JICE and Spring. The comparison shows that:
- JICE has many advantages when compared to the XML configuration module of Spring framework.
- The configuration module provides functionality also for areas not covered by JICE. This adds the value of the Spring solution.
The last part analyzes shortly how JICE and Spring could be used together. The intention is to find a solution that would combine the best of both worlds. This would help both Spring and JICE:
- The integration would enhance the configuration capabilities of Spring
- Making JICE work well with the vast functionality of the Spring framework would make JICE a more viable developer tool.
My knowledge about Spring framework is quite limited. So please forgive me if I have got some of the facts wrong. Feedback from this document is welcome and can be given at the forums:
2. Spring BeanFactory and ApplicationContext
Spring framework consists of multiple modules that ease the creation of J2EE applications. The modules provide tools for configuration management, Aspect oriented programming (AOP), transaction management, JDBC/Hibernate/JDO programming, web presentation layers, etc.
BeanFactory
and ApplicationContext
are the core of Spring.
-
org.springframework.beans.BeanFactory
- A factory for managed application objects (beans).
- An Interface that defines how the application objects are retrieved in an application.
- Implementations define how the application objects are created and initialized.
-
XmlBeanFactory
is a popular implementation that makes it possible to define the application objects in XML files.
-
org.springframework.context.ApplicationContext
- A subclass of
BeanFactory
- Provides handling of localized message resources, event propagation, hierarchical contexts, etc.
- Has various specialized subclasses, for e.g. web applications.
- The functionality of
XmlBeanFactory
is also used.
- A subclass of
The XmlBeanFactory
is almost identical to JICE. Both define an XML format for configuring Java objects and provide tools for processing the XML data into living Java objects.
ApplicationContext
provides mostly functionality for areas that JICE doesn't cover.
3. Object deployment models
Both JICE and the BeanFactory
of Spring create and initialize objects that are used in a Java application. JICE could in principle create the whole application itself, but usually only parts of the application objects are created by JICE.
From the view point of JICE and BeanFactory
, rest of the Java application is a client that requests objects from them. JICE and BeanFactory
create the objects on request and deliver them to the client.
The term object deployment model is used in this context for referring both to the way the clients retrieve the objects from JICE and BeanFactory
and to the way JICE and BeanFactory
internally deliver the objects to the client. An object is deployed when it is delivered to the client.
3.1. Object deployment model of BeanFactory
The process of obtaining an object (a bean in Spring) from BeanFactory
has two phases:
- obtain a
BeanFactory
instance - ask the
BeanFactory
for a bean by name
3.1.1. Obtaining a BeanFactory
instance
The client can create a BeanFactory
instance manually. First, the correct implementation needs to chosen (for example a XmlBeanFactory
or a XmlWebApplicationContext
). Then the BeanFactory
needs to be initialized. BeanFactory
implementations usually have various parameters to be configured like the location of the XML file and the BeanPostProcessors used, for example.
ApplicationContext
and its subclasses provide various automations for initializing BeanFactory
instances. In web applications for example, the context may be initialized by a servlet that reads a certain XML file in the WEB-INF
directory.
I didn't quite understand how the initialized BeanFactory
instances are actually obtained, however. Perhaps they are put in the ServletContext
in web applications.
3.1.2. Asking for a bean
A single BeanFactory
contains multiple beans. Once the client has got a hold of a BeanFactory
instance, it can obtain a specific application object by asking the bean by name.
BeanFactory
can deploy any bean either as a singleton or as a non-singleton. If a bean is deployed as a singleton, only a single instance of the bean will be created, and every query for the bean will yield the the same instance. A non-singleton deployment means that a new instance is created with the same settings on every query.
The deployment method of a bean can be specified in the XML configuration. The singleton method is more common than the non-singleton.
Spring supports also hierarchical bean definitions - Bean factories can be nested in a hierarchy and child factories may override the definitions of the parent factories.
3.2. Object deployment model in JICE
The object deployment model is very simple. Two phases can be identified:
- Specify the location of the JIC file and the parameters.
- Obtain the resulting object by letting the JIC Engine to process the file.
Everything else is left for the client application. JICE has no built-in support for locating the JIC files or loading them automatically. JICE doesn't store or cache the objects it creates as in the singleton-deployment pattern of BeanFactory
. Expressed in BeanFactory
terms, every object is a non-singleton.
In principle, every XML file specifies a single object, although this object can of course be a collection of objects. The XML files don't form a hierarchy, but the client may override definitions in a JIC file through parameters. Of course, the parameters could be defined in another JIC file, but JICE doesn't yet have any built-in support for achieving this.
3.3. Conclusions
The object deployment model of BeanFactory
is far more evolved than in JICE. Spring is a complete J2EE application framework, so it is natural that it covers more aspects.
JICE doesn't actually provide any object deployment model at all. JICE is specialized only in the XML-based configuration and provides no additional functionality. This approach has also advantages - JICE isn't locked to a single kind of deployment model - but it is obvious that as a configuration tool, JICE would profit from a more advanced object deployment model. Currently the applications using JICE have to provide the missing functionality by themselves.
4. XML configuration capabilities
This section studies how JICE and BeanFactory
initialize objects and how the object configuration is expressed in XML. Some basic object initialization capabilities are demonstrated by examples.
All the Spring examples are taken from the Spring documentation:
Spring Reference Documentation, version 1.1.2. http://www.springframework.org/docs/reference/index.html. (Referenced 2004-10-28)
4.1. Creating an object to be initialized
In Java, 3 different ways for object creation can be identified:
- Calling the constructor of a class with the
new
operation - Obtaining the object from a static factory method.
- Obtaining the object from a instance factory method.
All methods may or may not require some parameters to be present.
4.1.1. Calling an empty constructor
Spring:
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
JICE: The JICE version solution is almost identical to the Spring version. The constructor can be called explicitly by using new
operator or the constructor call can be derived from the class
attribute.
<bean class="examples.ExampleBean" instance="new examples.ExampleBean()"/>
4.1.2. Calling a constructor with parameters
Spring: Constructor arguments are defined with inner <constructor-arg> elements. Other beans are referenced with their id.
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg> <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
JICE: the attribute args
defines which of the child elements are constructor arguments.
<beanOne class="examples.AnotherBean"/>
<beanTwo class="examples.YetAnotherBean"/>
<integer class="int">1</integer>
</exampleBean>
Also objects defined outside an element may be referenced by their name if they are declared to be variables. Because JIC Engine processes the elements in sequence, the referenced objects must be defined before they are used.
<anotherExampleBean class="examples.AnotherBean"/>
<yetAnotherBean class="examples.YetAnotherBean"/>
<exampleBean class="examples.ExampleBean" args="beanOne, beanTwo, integer">
<beanOne class="examples.AnotherBean" instance="anotherExampleBean"/>
<beanTwo class="examples.AnotherBean" instance="yetAnotherBean"/>
<integer class="int">1</integer>
</exampleBean>
</container>
Objects can also be referenced directly in the args
attribute. There's no need for the inner elements.
<anotherExampleBean class="examples.AnotherBean"/>
<yetAnotherBean class="examples.YetAnotherBean"/>
<exampleBean class="examples.ExampleBean" args="anoterExampleBean, yetAnotherBean, integer">
<integer class="int">1</integer>
</exampleBean>
</container>
4.1.3. Obtaining an object from a static factory method without parameters
Spring: Static factory method is defined with the attributes class
and factory-method
. The semantics of the class
attribute has changed - it refers now to the class containing the factory method, not the class of the bean instance.
<bean id="exampleBean" class="examples.ExampleBean2" factory-method="createInstance"/>
JICE: The method call is expressed in the attribute instance
.
4.1.4. Obtaining an object from a static factory method with parameters
Spring: The parameters for the factory method are provided with the <constructor-arg> elements.
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg> <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
JICE: parameters are defined as part of the method call in the instance
attribute.
<beanOne class="examples.AnotherBean"/>
<beanTwo class="examples.YetAnotherBean"/>
<integer class="int">1</integer>
</exampleBean>
Again, argument objects may also be defined outside the element <exampleBean>.
<anotherExampleBean class="examples.AnotherBean"/>
<yetAnotherBean class="examples.YetAnotherBean"/>
<exampleBean class="examples.ExampleBean" instance="examples.ExampleBean.createInstance(beanOne, beanTwo, integer)">
<integer class="int">1</integer>
</exampleBean>
</container>
4.1.5. Obtaining an object from an instance factory method
Spring: The attributes factory-bean
and factory-method
are used.
<!-- The factory bean, which contains a method called createInstance --> <bean id="myFactoryBean" class="..."> ... </bean> <!-- The bean to be created via the factory bean --> <bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
JICE: The call to the instance factory method is specified in the instance
attribute, like the case with the static factory.
<exampleBean class="examples.ExampleBean" instance="myFactoryBean.createInstance()"/>
The configuration of the factory instance can also be defined inside the <exampleBean>.
<myFactoryBean class="..."> </myFactoryBean>
</exampleBean>
4.2. Calling methods of an object to be initialized
After the object is obtained, the initialization of the object may involve method invocations. The methods to be invoked can separated to property setters and other methods.
4.2.1. Setting properties
Setting a property means calling the respective setXXX()
method of the object.
Spring: The <property> element is used for setting properties. The element <value> specifies the value of the property. Spring uses property editors for converting the String inside the <value> element into the required Java object.
<bean id="exampleBean" class="examples.ExampleBean"> <property name="beanOne"><ref bean="anotherExampleBean"/></property> <property name="beanTwo"><ref bean="yetAnotherBean"/></property> <property name="integerProperty"><value>1</value></property> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
JICE: Inner elements that are not constructor parameters are used for setting properties. All conversions from String values to other objects must be set explicitly. In this case, the attribute class="int"
declares, that the CDATA section of the <integerProperty> is converted to java.lang.Integer
and ultimately to int
.
<beanOne class="examples.AnotherBean"/>
<beanTwo class="examples.YetAnotherBean"/>
<integerProperty class="int">1</integerProperty>
</exampleBean>
4.2.2. Calling a method
Spring: Only a single method called initialization method can be called during the initialization of a bean. (don't know whether the method can be given parameters or not. the example didn't mention them)
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
JICE: The method call is defined with a child element that has the attribute action
.
<call-init action="init()"/>
</exampleInitBean>
The action
attribute can be used for calling any method with any kind of parameters. There is also no restrictions on how many method calls there are.
Here is an example that calls the add(int,int)
method of class java.util.Calendar
,
<lenient class="boolean">true</lenient>
<addAYear action="add(field,amount)">
<field class="int" instance="java.util.Calendar.YEAR"/>
<amount class="int">1</amount>
</addAYear>
<addAMonth action="add(field,amount)">
<field class="int" instance="java.util.Calendar.MONTH"/>
<amount class="int">1</amount>
</addAMonth>
</calendar>
4.3. Obtaining the return-value of a method call
Spring: This example shows how to obtain the value of the system property java.version
.
<bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetClass"><value>java.lang.System</value></property> <property name="targetMethod"><value>getProperties</value></property> </bean> <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject"><ref local="sysProps"/></property> <property name="targetMethod"><value>getProperty</value></property> <property name="arguments"> <list> <value>java.version</value> </list> </property> </bean>
JICE: The same situation is handled with the following JIC script:
<systemProperties class="java.util.Properties" instance="java.lang.System.getProperties()"/>
<propertyName>java.version</propertyName>
</javaVersion>
4.4. Obtaining a value of a static field
Spring: not possible.
JICE: The static field can be specified in the instance
attribute:
4.5. Create an array
Spring: no array support.
JICE: element type array constructs an array. Both object and primitive arrays are supported.
<word>hello</word>
<word>world</word>
</words>
<number class="int">1</number>
<number class="int">2</number>
<number class="int">3</number>
</numbers>
4.6. Create a collection
Both Spring and JICE have structures for creating lists and maps. Spring has special <list> and <map> elements. In JICE a list or map can be populated manually (by calling add()
or put()
) or by using built-in element types.
Examples are not shown - there are enough examples already.
4.7. Null values
Spring: There is an element <null> that can be used for setting the value of a property or constructor parameter to null.
JICE: No null support.
4.8. Overriding definitions in the XML file
Spring: Bean factories can form an hierarchy. Child factories may override definitions of the parent factory.
JICE: Only elements that are marked to be overridable can be overridden. Client provides the overriding values i.e. build parameters to the JIC Engine when the processing of a XML file starts.
4.9. Conditionality Support
Spring: no.
JICE: has a simple if
and switch
structures that make it possible to define some logic in the JIC Files.
4.10. User defined types
Spring: Bean definitions may have a parent definition. The child definition inherits the definitions of the parent. This makes the parent definition function as a custom type.
JICE: user-defined factories may be declared and used in JIC Files. factories resemble functions - they receive parameters and return an object.
4.11. Summary
Feature | JICE | Spring (XmlBeanFactory ) |
---|---|---|
Can instantiate any class with the new operation. |
yes. | yes. |
Can obtain an object from a static factory method. | yes. | yes. |
Can obtain an object from an instance factory method. | yes. | yes. |
Can set any property by calling a respective setXXX() method. |
yes. | yes. |
Can call any public methods of an object. | yes. | no. (Can call only a single initialization method without parameters.) |
Can call a desctruction method. | no. | yes. (Can call a single desctruction method without parameters, if the bean is deployed as a singleton.) |
Can obtain the return-value of a method call. | yes. | yes. |
Constants support - can obtain the value of a static field. | yes. | no. |
Can create object and primitive arrays | yes. | no. |
Can create and populate collections | yes. | yes. |
null support | no. | yes. |
Definitions in the XML file can be overridden. | partial. (Only elements that are declared to be overridable can be overridden) | yes. |
Conditionality | yes. | no. |
User defined types. | yes. | yes. |
4.12. Conclusions
4.12.8. Spring is biased towards beans
The XML configuration capabilities of Spring are biased towards beans. It is easy to instantiate an object and set its properties, but there is a poor support for non-bean-like aspects:
- It is not possible to call methods that don't adhere to the bean-like proporty-setter standard.
- Arrays can't be created.
- Constants can't be used.
- Quite a lot of XML code needs to written in order to obtain the return-value of a method call (see example above).
Although the bean model is popular, only a part of the existing Java classes are beans. Non-bean-like features are also needed.
JICE supports beans but is not limited to them. JICE handles both bean-like and non-bean-like situations equally well. JICE has much more object initialization features than Spring.
Spring has 2 important features that JICE doesn't currently provide:
- Support for null values.
- User defined types.
These features would certainly be useful in JICE also.
4.12.9. XML code of JICE is closer to the application domain
The XML format of Spring contains elements like <bean>, <property>, <value>, <ref>, <constructor-arg>, etc. The element names are abstract and not related to the application domain - the application that is being configured probably doesn't have classes or properties named bean, property, value, constructor-arg.
In JICE the XML element names depend on the API of the application and on the naming convention of the developer. The element names are always related to the application domain. This makes the XML data more descriptive.
5. Using JICE and Spring together
Because the XML configuration capabilities of JICE are better than those of Spring, JICE could be used instead of the Spring's XmlBeanFactory
component.
The BeanFactory
and ApplicationContext
have no direct dependencies on other Spring modules. The other modules are used through their API: the classes are referred from the XML configuration files.
Because JICE can also configure any existing Java API, the other Spring modules can be used from JIC files in the same manner as they are now used from the XML files of XmlBeanFactory
. Instead of writing an XML file for XmlBeanFactory
, a JIC file would be written.
Of course, the objects defined in the JIC files must be made available. This is the most problematic aspect in the integration of JICE and Spring. There are two alternatives:
- loose integration: JICE is used through its own interface.
-
JICE plugin: JICE is used through the
BeanFactory
interface. A JICE-based implementation of theBeanFactory
would be created (aJICEBeanFactory
or something).
The first one is simpler and can be done already. However, many of the existing Java classes using Spring are already constructed based on the BeanFactory
and ApplicationContext
interfaces. All this code would have to be refactored, which makes the alternative 1. a poor choice. (especially if the other Spring modules assume that BeanFactory
and ApplicationContext
are used for configuration. I haven't studied this yet.)
The plugin-alternative would provide a better solution. The plugin would just have to created first. There isn't currently any clear plan on how and when such a plugin would be implemented.
5.1. About implementing the JICE plugin
If only BeanFactory
interface would need to be created, the plugin would be easy to implement.
However, the BeanFactory
interface has multiple subinterfaces, ApplicationContext
being probably the most important one. Perhaps a plugin implementing some of the subinterfaces would be needed? I don't yet know whether this would be difficult or not.
The biggest obstacles in creating the plugin are probably related to:
- Singleton/non-singleton deployment model that is not supported by JICE.
- Hierarchical bean factories.
If you have any suggestions or ideas on how such a JICE plugin should be implemented, please share your thoughts on the forum: