Topics Covered:
- What is Inversion of Control and Dependency Injection?
- Lets write our own simple IOC container form scratch using Java and XML.
- What are some of the practical uses of IOC and DI?
In figure 1 we have an arbitrary object called “AnObject” (item 1 in the image) and an arbitrary XML configuration defining our object attributes (item 2 in the image). Inversion of Control describes an object creation flow/process in which objects are created and their attributes set or loaded dynamically at run-time via an IOC container. This is achieved using Dependency Injection (a design pattern).
There are three types of Dependency Injection:
- Interface injection: Is used when an object is defined by an interface that it must implemented in order to inject dependencies at runtime.
- Setter injection: Is used to refer to an object exposing a setter method(s) to inject dependencies at run-time.
- Constructor injection: Generally the same concept as setter injection except we utilize the constructor instead (this is what we use in our example below).
The example below has been written using all three techniques (setter, constructor, and interface) so you can get a better idea of the differences. The displayed and discussed code is the one which uses Constructor Injection, but all three versions are available for download at the end of the article.
For our example we will use Dependency Injection to inject object attributes defined in an XML configuration file (IOC-application-context.xml).
Figure 2 above contains the components of our simple IOC Container application. We have the container itself (DevHubContainer.java), our objects Band.java and BandMember.java, and the configuration file defining our object dependencies and attributes (IOC-application-context.xml).
IOC-application-context.xml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<beans>
<bean id="Robert" class="fm.devhub.objects.BandMember">
<property name="name" value="Robert Plant" />
</bean>
<bean id="Jimmy" class="fm.devhub.objects.BandMember">
<property name="name" value="Jimmy Page" />
</bean>
<bean id="John" class="fm.devhub.objects.BandMember">
<property name="name" value="John Bonham" />
</bean>
<bean id="Jones" class="fm.devhub.objects.BandMember">
<property name="name" value="John Paul Jones" />
</bean>
<bean id="Band" class="fm.devhub.objects.Band">
<property name="name" value="Led Zeppelin" />
<property name="label" value="Atlantic" />
<property name="genere" value="Rock" />
<property name="singer" bean-reference="Robert" ref-class="fm.devhub.objects.BandMember"/>
<property name="bass" bean-reference="Jones" ref-class="fm.devhub.objects.BandMember" />
<property name="guitarist" bean-reference="Jimmy" ref-class="fm.devhub.objects.BandMember"/>
<property name="drummer" bean-reference="John" ref-class="fm.devhub.objects.BandMember"/>
</bean>
</beans> |
Lines 2-13:
Here we define our simple Objects, we have members of a musical band belonging to the package fm.devhub.objects.BandMember, and one attribute defined which is the name of the band member.
Lines 14-22:
We define our band Object. Its basically is composed of some attributes defining the name of the band etc. But also notice it defines some Object references, band-members. So when we construct our Band object, the IOC Container will know to include Objecs of type BandMember as well.
BandMember.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package fm.devhub.objects;
import java.util.ArrayList;
/*
* This class resembles a Band Member Bean or POJO.
* Properties for our Band Member are injected using
* our IOC Container & Constructor Injection.
*/
public class BandMember {
private String name;
public BandMember(){
}
public BandMember ( ArrayList<String> name ){
this.name = (String) name.get(0);
}
public String toString() {
return "Member Name: " + name;
}
} |
Here we have a simple Object called BandMember, it has one attribute which defines the name of the band member. Because this example is using Constructor Injection, I didnt include any set or get methods. The object is constructed by our IOC container which injects the constructor value at runtime based on what is defined in IOC-application-context.xml.
Band.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package fm.devhub.objects;
import java.util.ArrayList;
/*
* This class resembles a Band Bean or POJO.
* Properties for our Band Member are injected using
* our IOC Containe and Constructor
* Injection.
*/
public class Band {
private String name;
private String label;
private String genere;
private BandMember singer;
private BandMember bass;
private BandMember guitarist;
private BandMember drummer;
public Band() {
}
public Band( ArrayList<Object> params ) {
this.name = params.get(0).toString();
this.label = params.get(1).toString();
this.genere = params.get(2).toString();
this.singer = (BandMember) params.get(3);
this.bass = (BandMember) params.get(4);
this.guitarist = (BandMember) params.get(5);
this.drummer = (BandMember) params.get(6);
}
public String toString() {
return "Name: " + name + "\nLabel: " + label + "\nGenre: " + genere + "\nSinger " + singer
+ "\nBass " + bass + "\nGuitarist " + guitarist + "\nDrummer "
+ drummer;
}
} |
Band is very similar to BandMember. The only difference is that there are more attributes being set. Once again our IOC Container will inject those values at runtime based on the relationships defined in IOC-application-context.xml.
DevHubContainer.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
package fm.devhub.ioccontainer;
//Import the Java classes
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
//Import the Reflection classes
import java.lang.reflect.Constructor;
//Import the JDOM classes
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
/*
* This is our simple IOC Container, it parses
* IOC-application-context.xml and uses DI
* to create and assemble the beans.
*/
public class DevHubContainer {
/**
* Keeps track of all our bean nodes.
*/
private List<Object> beanList;
/**
* Creates a new DevHubContainer that is configured by the specified XML
* filename, we use JDOM SAX to traverse through our XML document.
*/
public DevHubContainer(String application_context) {
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(application_context);
Element root = doc.getRootElement();
this.beanList = root.getChildren("bean");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns a configured bean based on the name for example: <bean id="Band"
* class="fm.devhub.objects.Band"> has the name 'Band'
*/
public Object getBean(String name) {
// Iterate over our bean list, this was
// populated during the instantiation of our
// IOC container.
for (Iterator i = this.beanList.iterator(); i.hasNext();) {
Element bean = (Element) i.next();
String id = bean.getAttributeValue("id");
if (id.equals(name)) {
try {
String className = bean.getAttributeValue("class");
List propertyList = bean.getChildren("property");
ArrayList elementValue = new ArrayList();
if (propertyList.size() > 0) {
for (Iterator it = propertyList.iterator(); it.hasNext();) {
Element propertyElement = (Element) it.next();
String propertyValue = propertyElement
.getAttributeValue("value");
String propertyReference = null;
if (propertyValue == null) {
propertyReference = propertyElement.getAttributeValue("bean-reference");
Class refbeanClass = Class.forName( propertyElement.getAttributeValue("ref-class") );
Constructor[] refconstruct = refbeanClass.getConstructors();
ArrayList<String> propertyReferenceValue = new ArrayList();
propertyReferenceValue.add( propertyReference );
Object refbeanInstance = refconstruct[1].newInstance(propertyReferenceValue);
elementValue.add(refbeanInstance);
} else {
elementValue.add(propertyValue);
}
}
}
Class beanClass = Class.forName(className);
Constructor[] construct = beanClass.getConstructors();
Object beanInstance = construct[1].newInstance(elementValue);
return beanInstance;
} catch (Exception e) {
e.printStackTrace();
}
}
}
// If no matching objects are found, then return null
return null;
}
} |
Lines 35-44:
We build a parser using JDOM and pass it our IOC-application-context xml.
Lines 60-90:
Here is the core of our IOC Container, we iterate over our application context, and create the objects defined. The objects are created very simply as follows:
Class beanClass = Class.forName(className);
Constructor[] construct = beanClass.getConstructors();
Object beanInstance = construct[1].newInstance(elementValue);
elementValue is an ArrayList containing all of the values that need to be set in the objects constructor.
IOCContainerTest.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package fm.devhub.test;
import fm.devhub.ioccontainer.DevHubContainer;
import fm.devhub.objects.Band;
import fm.devhub.objects.BandMember;
public class IOCContainerTest {
public static void main( String[] args ) {
/*
* We instantiate our IOC Container, and load our application-context
* Then we use IOCContainer.getBean() to inject our Band and BandMember bean
* configuration attributes.
*/
DevHubContainer IOCContainer = new DevHubContainer( "C:\\IOC-application-context.xml" );
BandMember Jimmy = ( BandMember )IOCContainer.getBean( "Jimmy" );
BandMember Robert = ( BandMember )IOCContainer.getBean( "Robert" );
BandMember John = ( BandMember )IOCContainer.getBean( "John" );
BandMember Jones = ( BandMember )IOCContainer.getBean( "Jones" );
System.out.println( "**** Test Band Member Beans****" );
System.out.println( Jimmy.toString() );
System.out.println( Robert.toString() );
System.out.println( John.toString() );
System.out.println( Jones.toString() );
Band LedZepplin = (Band) IOCContainer.getBean("Band");
System.out.println( "**** Test Band Bean****" );
System.out.println(LedZepplin);
}
} |
The test case simply tests all of the objects defined in IOC-application-context.xml. Our simplest object definitions are the BandMembers where we only a name parameter is set. Slightly more complex is the Band object which is composed of a few attributes and a reference to the BandMembers.
Below is the output that should be generated when you run the test case.
Test case output:
Member Name: Jimmy Page Member Name: Robert Plant Member Name: John Bonham Member Name: John Paul Jones **** Test Band Bean**** Name: Led Zeppelin Label: Atlantic Genre: Rock Singer Member Name: Robert Plant Bass Member Name: John Paul Jones Guitarist Member Name: Jimmy Page Drummer Member Name: John Bonham
Errors I ran into while writing the example:
java.lang.InstantiationException: fm.devhub.objects.BandMember at java.lang.Class.newInstance0(Class.java:340) at java.lang.Class.newInstance(Class.java:308) at fm.devhub.objects.BandMember.main(BandMember.java:29) Exception in thread "main" java.lang.NullPointerException at fm.devhub.objects.BandMember.main(BandMember.java:30) I ran into this error because i didn't use a default constructor in my BandMember class. The default constructor is automatically created by java only when no other constructors are present.
When is an IOC Container needed?
The answer to this question is going to vary depending on what you are trying to accomplish as well as your personal programming style and requirements.
Generally you should consider adding Dependency Injection and IOC Container functionality in the following scenarios:
- You need to inject configuration data into an object (for example database connection configuration).
- You need to inject different implementations of the same object.
- You need to inject the same dependency into objects
Also, a very powerful side effect using an IOC Container and DI is that it caninherently simplify testing. Envision being able to mock up objects as needed for testing in your configuration (application context) file vs. having to connect to a database or call/stub a service. Also testing objects under multiple configurations is simple using DI and an IOC Container.
You do not need Dependency Injection in the following scenarios:
- You will not need different configurations for an object.
- You will not need different implementations for an object.
That said, IOC containers such as Spring IOC offer many useful features which are enabled by DI. After reading this article I would advise familiarizing yourself with Spring IOC (or any other main stream IOC framework) simply because it will give you better insight into best practices and industry standards for IOC implementations and testing approaches.
What are some popular IOC Containers?
- Spring IOC
- Pico Container
Download Example Source:
Click here to download example source.
Additional Reading:
Factory Pattern vs. Dependency Injection
Why do i need an IOC container as opposed to straightforward DI code
Martin Fowler: Injection


