Using parent/child applications contexts in the Spring framework
The problem :
We would like to have an application that has two layers :
(1) a non-dynamic layer
(2) dynamic layer
The non-dynamic layer could include things like :
* database sessions
* mail services
* templating engines
* basic beans/components
etc.
As to keep things simple, a bean/component belongs to the non-dynamic layer if you are ok
with changing only through redeploys. The examples mentioned (ie. database sessions, etc.)
clearly belong to this category as we almost never want to change the database session settings
while the application runs.
The dynamic layer could include things like :
* configurable modules
* rule processing chains
* logging
The rule of thumb would be : anything that you might want to change while the application runs
(without any redeploy) would belong to the dynamic layer.
The Spring JAVA framework can help a lot with this. There are mainly 3 ways to do it :
(1) multiple hierarchical application contexts
(2) dynamic language support
(3) OSGI
The easiest (and best performing) solution is to use multiple hierarchical application contexts.
1. Creating hierarhical application contexts
The principle behind this is the fact that the Spring application context is similar to a
tree, builded from bottom up. As such every application context that we load (an XML file content
or just plain String) can have another application context as parent.
AbstractApplicationContext parentContext = new ClassPathXmlApplicationContext(new String[]{“classpath:META-INF/spring/applicationContext-job-sample1.xml”});
AbstractApplicationContext childContext = new ClassPathXmlApplicationContext(new String[]{“classpath:META-INF/spring/applicationContext-job-sample1.xml”},parentContext);
The fact that the tree is builded bottom up is a usefull metaphor to remember as it constrains the visibility of the application contexts (and the beans defined inside of them):
the child can see the parent, the parent cannot see the child. This makes sense as when the parent context was loaded, there was no child context available.
As such the visibility/scope is going only one way. You cannot use the parent context to get a bean from the child context. Only the other way around : use the child context to
get a bean from the parent context.
2. Managing multiple contexts – the simplest way to go
The easiest/cleanest way to work with this situation is to think of the child application context as a “second class citizen”. As such it can contain beans that
refer to the parent/stable context. Also you avoid cross-refering/cross-using beans from one child context to another (this is not possible through the Spring
context application loader, but it could be done trough different hacks in your code).
A good example of the child application context would be that of a job that gets configured as a chain of multiple resources/beans available in the parent :
[Parent context]
<bean id=”parentTask1 />
<bean id=”parentTask2 />
<bean id=”databaseSession” />
[Child context 1]
<bean id=”job1″ class=”com.example.Job” >
<util:list name=”tasks” >
<value ref=”parentTask1″ />
<value ref=”parentTask2″ />
</util:list>
<property name=”databaseSession” ref=”parentDatabaseSession” />
</bean>
[Child context 2]
<bean id=”job2″ class=”com.example.Job” >
<util:list name=”tasks” >
<value ref=”parentTask1″ />
<value ref=”parentTask2″ />
</util:list>
<property name=”databaseSession” ref=”parentDatabaseSession” />
</bean>
As you can see the child context just define “job plans”, which can be fancifull assemblies of existing parent beans.
Whenever you want to call a job (ie. by id) you just need to know in which child context was the job defined and then
use that particular context to instantiate the bean. The straightforward way to do this is to keep a Map<String,AbstractApplicationContext>
somewhere accesible in the parent context and whenever you add a new child context, update the map with all the beans from the child context
that you know you will need to call directly (ie. in the above example it would make sense to get all the beans that are an instance of the
com.example.Job class as I’m sure that I will call just job beans directly)
2.1 Load, Refresh & Close
The main reson why we want to have child application contexts is because we want our jobs to be flexible. As such we need to be able to create, update
& delete jobs without any redeploy.This is done straightforward in our situation :
Create a child application context :
AbstractApplicationContext childContext = new ClassPathXmlApplicationContext(new String[]{“classpath:META-INF/spring/applicationContext-job-sample1.xml”},parentContext);
Use the child application context :
Job job = (Job) chilContext.getBean(“job1″)
Refresh the child application context :
childContext.refresh();
Close the child application context :
childContext.close();
If you have some mechanism to update the child application context XML files, all you need to do is to refresh the child application (or close and
load them), and you get a new “version” of the jobs.
3. Pitfalls
First : Don’t cross reference beans from two different child contexts. What happens when you end up closing one of the contexts and the other one needs it?
Second : Don’t use beans from the child context in the parent context. You cannot do this through Spring directly, but you can still assign it through you code. When the child application gets closed or refreshed you will
still keep references from the old version.

leave a comment