Inversion of Control Container
One of my tasks for this week was to create a very basic IoC Container library (Spring is what you can use to do this in Java if you don’t want to write your own…)
An IoC Container is basically an object that ‘manages’ the injection of different dependent classes in your application. The idea is to move away from a system in which you have to manually initialise instances of all your classes and inject them in the right places in all the other classes. The problem with this is that it means that you always need to remember to inject the right things when you update any classes’ dependencies. With an IoC Container, when you write a new class, you simply register it with the Container
The IoC Container holds on to references to classes (or an instance if the particular class is supposed to be a singleton) that we register with it. We can then pass our main class into the Container’s construct function, and it will automatically construct it using the registered classes. The way it does this is by using Java’s Reflection API, which allows the Container to retrieve the class’s constructor, the types of the constructor’s parameters, and to invoke the constructor with the correct parameters using the class references that we registered with it.
So let’s look at some code to see what doing this actually looks like. I’m just going to go through the Container’s construct method.
Here are the first few lines. The method takes a reference to a Class (it has to use the generic <T> because it’s supposed to work with any Class when run). We give the Class argument the name clazz because class is a keyword in Java:
1 public <T> T construct(Class<T> clazz) {
2 Constructor[] constructors = clazz.getConstructors();
3 Constructor<T> constructor = (Constructor<T>)constructors[0];
4 Class[] parameterTypes = constructor.getParameterTypes();
5 ...
6 ...
7 }On line 2 we use the method getConstructors to get a Constructor[] for the class that we’re trying to construct. Then on line 3 we extract the first Constructor object from the list and cast it using (Constructor<T>)to be a generic Constructor with the type of the Class we’re constructing. I’m did it this way because I’m making a very basic IoC container, so I’m just assuming that all Classes have a single constructor that I’m interested in.
Line 4 is where we find out the types of the classes that the class we’re constructing needs to be injected with. We call the method getParameterTypes on constructor. This method looks at the Constructor object and returns a Class[] of the classes needed to instantiate it.
Now that we have the parameter types, here’s the method with the last two lines included:
1 public <T> T construct(Class<T> clazz) {
2 Constructor[] constructors = clazz.getConstructors();
3 Constructor<T> constructor = (Constructor<T>)constructors[0];
4 Class[] parameterTypes = constructor.getParameterTypes();
5 Object[] parameters = makeParameters(parameterTypes);
6 return constructor.newInstance(parameters);
7 }On line 5 I’m making a call to the method makeParameters, which I defined on the Container class. This method turns the Class[] of parameters into actual instances of the classes. It does this by looking through the Container object’s list of registered classes and instances to find or construct the parameters in the Class[]. If you’re interested in seeing the full Container class code to look over how I stored the registered classes and instances, and how I initialise them in makeParameters, check out https://github.com/onlyskin/java-ioc/blob/master/src/main/java/ioc/Container.java
Finally, line 6 is where we actually construct the instance of clazz and return it. We construct it by calling the method newInstance on the constructor instance that we got out in line 3. newInstance takes an Object[] as it’s single argument. This Object[] is the list of instantiated parameters that the constructor requires — conveniently makeParameters also returned an Object[]… newInstance then simply constructs an instance of the class clazz using this Object[] as the parameters.
This is pretty much everything for our very basic IoC Container construct method. Thanks for reading!
For a friendly introduction to Java Reflection API: http://tutorials.jenkov.com/java-reflection/index.html
My full code on GitHub: https://github.com/onlyskin/java-ioc
