Inversion of Control (IoC) is a design process of externalizing the construction and management of objects. It outsources the creation and management of the objects; the outsourcing is handled by an object factory.
Let’s look at a real-world project to understand IoC better. Our app is going to be a teaching platform where the student selects a teacher of a particular subject and asks for some notes, homework, or questions to solve on a daily basis. Our app works for different types of teachers, like history teachers, physics teachers, etc., meaning it should be configurable.
MyApp.java: main method
HistoryTeacher.java: simple implementation
Teacher.java: interface class
TrackTeacher.java: switching different teachers
To get started, create a Java project in Eclipse. Make sure it is a JAVA EE project, and as our development framework is Spring, you need to make sure to install all the libraries important for getting started with a Spring project. After doing this, create a new package in your src folder (I named mine com.nikhil.IoC), and create an empty POJO file named HistoryTeacher.java. Create a method called getHomework where we just return a string.
1
2
3
4
5
6
7
8
package com.nikhil.IoC;
public class HistoryTeacher {
public String getHomework() {
return "Page number 30 to 35 all the questions";
}
}
We want to work with different types of teachers within our app, but the above is only configured for HistoryTeacher. So, what do we do now? We create an interface that includes common methods that every teacher should include. So every teacher is going to have a method called getHomework(). Let’s create this method Teacher.java.
1
2
3
4
5
6
7
package com.nikhil.IoC;
public interface Teacher {
public String getHomework();
}
We can make our HistoryTeacher implement our Teacher interface.
1
2
3
4
5
6
7
8
9
package com.nikhil.IoC;
public class HistoryTeacher implements Teacher {
@Override
public String getHomework() {
return "Page number 30 to 35 all the questions";
}
}
Now go ahead and create our main class, MyApp.java, and reference the interface class teacher with our HistoryTeacher and class the getHomework method. You will see something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.nikhil.IoC;
public class MyApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
Teacher teacher = new HistoryTeacher();
System.out.println(teacher.getHomework());
}
}
Now we want to make our app support different types of teachers, so instead of using HistoryTeacher, we will use TrackTeacher in our main application for reference. Before using it let’s create it, in our src IoC package com.nikhil.IoC;
1
2
3
4
5
6
7
public class TrackTeacher implements Teacher {
@Override
public String getHomework() {
return "solve the previous year questions";
}
}
So what we have built is a rough prototype, as now it works for different types of teachers. But, the other requirement, that our app should be configurable, is yet to be handled because everything is hardcoded. If we could read the actual implementation from a config file, then we may swap by only changing a config file instead of changing the source code.
So this is where Spring plays its part. Spring provides an object factory so our application can talk to Spring, like asking for an object based on a configuration file. Spring will provide an appropriate implementation, so our app is configurable and provides full support based on our application requirements.
Spring container can be taken as the core of Spring framework. It deals with the creation and management of objects known as inversion of control and injection of object dependencies called dependency injection.
We will cover IoC in this article and dependency injection in a future article.
Now we configure our Spring container. We have three different approaches,
Java source code
Java annotations
XML configuration file
In this article, we will discuss XML configuration file. Our process has three main steps. First, we will configure our Spring beans, then create a Spring container, and then retrieve the beans from the container.
Spring container is generally known as applicationContext. I am attaching a default code for applicationContext file to get started, so go ahead and paste this into your main src folder.
applicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Define your beans here --> </beans>
Now let’s go ahead and define our beans. The first attribute is an ID “myTeacher” that works as an alias which we will use in our application. Then we provide the class which is “com.nikhil.IoC.TrackTeacher”.
1 2 3
<bean id="myTeacher" class="com.nikhil.IoC.TrackTeacher"> </bean>
So we have configured our application, for the next steps:
Load the spring configuration file
Retrieve bean from Spring container
Call methods on the bean
Close the context
After doing all the above steps in our main file and running our main file, then:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.nikhil.IoC;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Teacher teacher = context.getBean("myTeacher", Teacher.class);
System.out.println(teacher.getHomework());
context.close();
}
}
To support our claim we will now change our config file to point to HistoryTeacher and see how our configuration works with different implementations.
There is no need to change the source code as it is reading everything from the configuration file. Our app is configurable by just simply changing the config file and running it without modifying any source code.