Dependency Injection (DI) allows a program design to follow the dependency inversion principle. The client delegates to calls to another object the responsibility of providing its dependencies.
In simpler terms, in DI we outsource the construction and injection of our object to an external entity. In Spring it is an object factory - like our teacher object, this teacher object may have some additional dependencies.
As we know, there are two major functions of a container, creating and managing objects (inversion of control) and injecting object dependencies (dependency injection). Here we will discuss DI. For more on IoC please check out this article.
In our Inversion of Control using XML configuration we used an example of a teacher interface, here we will add dependency injection to the same.
Let’s have a recap,
We have a teacher class that provides daily homework, now our teacher will also provide daily quotes, like some motivational quotes. Here we will use a helper service called QuoteService which will work as a dependency in order to provide the daily quotes.
Two most common ways to inject dependencies:
Construction injection
Setter injection
In construction injection, we define the dependency using interface and class. Now, create a new interface called QuoteService.java, and define one method called getQuote() which returns a string.
1
2
3
4
package com.nikhil.IoC;
public interface QuoteService {
public String getQuote();
}
Now we will create a class to implement this interface, named MotivationalQuoteService.java. Go ahead and implement the only function getQuote like below:
1
2
3
4
5
6
7
8
package com.nikhil.IoC;
public class MotivationalQuoteService implements QuoteService {
@Override
public String getQuote() {
return "Inspiration does exist, but it must find you working";
}
}
Now let’s add this new method to the Teacher interface, as we want our teacher to also give daily quotes. We add getDailyQuote and then add the unimplemented methods to the other implementing classes.
public String getDailyQuotes();
TrackTeacher.java:
1
2
3
4
@Override
public String getDailyQuotes() {
return "Inspiration does exist, but it must find you working";
}
Now for the next step, create a constructor in our class for injections, as we have done for the historyTeacher class.
HistoryTeacher.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// define a private field for the dependency
private QuoteService quoteService;
// define a constructor for dependency injection
public HistoryTeacher(QuoteService quoteService) {
this.quoteService = quoteService;
}
@Override
public String getHomework() {
return "Page number 30 to 35 all the questions";
}
@Override
public String getDailyQuotes() {
return quoteService.getQuote();
}
For the last step, configure the dependency injection in our config file. We define a bean here for this dependency, our quote service.
1 2 3
<bean id="myQuoteService" class="com.nikhil.IoC.MotivationalQuoteService"> </bean>
Here we provide id and class to our bean. Now, to use construction injection we make use of constructor-arg, and provide ref of the bean with ID “myQuote”.
1 2 3 4 5 6
<bean id="myTeacher" class="com.nikhil.IoC.HistoryTeacher"> <!-- set-up constructor injection--> <constructor-arg ref="myQuoteService" /> </bean>
This is going to create our teacher object. Call the constructor and pass in that fortune reference myQuote, which is effectively MotivationalQuoteService.
Next we move to our main class and retrieve the bean from the container myTeacher. Now the bean actually has some dependencies. Spring factory creates all the appropriate beans and dependencies and injects those dependencies. Let’s call our new method getDailyQuote().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.nikhil.IoC;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Teacher teacher = context.getBean("myTeacher", Teacher.class);
System.out.println(teacher.getHomework());
System.out.println(teacher.getDailyQuotes());
context.close();
}
}
Spring framework injects dependencies by calling setter methods of a class and then configuring the dependency injection in the Spring config file.
Create a new class, PhysicsTeacher.java. Here we will define our setter methods.
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
package com.nikhil.IoC;
public class PhysicsTeacher implements Teacher {
private QuoteService quoteService;
public PhysicsTeacher() {
System.out.println("no-arg: physics");
}
//setter method
public void setQuoteService(QuoteService quoteService) {
this.quoteService = quoteService;
}
@Override
public String getHomework() {
return "DO 10 questions on rectilinear motion";
}
@Override
public String getDailyQuotes() {
return quoteService.getQuote();
}
}
Now we should configure our dependencies. To do that, head over to the config file and introduce a new bean with id “myPhysicsTeacher”, classpath. To set up setter injection we have a property which we name as per our service, quoteService, and value, myQuote. Doing this calls the setter function for the corresponding class using the name, like if the name is quoteService this calls setQuoteService.
1 2 3 4 5 6
<bean id="myPhysicsTeacher" class="com.nikhil.IoC.PhysicsTeacher"> <!-- set-up setter injection--> <property name="quoteService" ref="myQuoteService"/> </bean>
Now go ahead and call these methods on your main class and see the results.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.nikhil.IoC;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Teacher teacher = context.getBean("myPhysicsTeacher", Teacher.class);
System.out.println(teacher.getHomework());
System.out.println(teacher.getDailyQuotes());
context.close();
}
}
As you can see, all the required methods are called behind the scenes by the Spring framework. Making use of setter injection they inject myQuoteService, then we can use our teacher and call methods.
We can also inject lateral values using setter injections, like email, name, etc., using the below code:
1 2 3 4 5 6 7 8 9 10 11
<bean id="myPhysicsTeacher" class="com.nikhil.IoC.PhysicsTeacher"> <!-- set-up setter injection--> <property name="quoteService" ref="myQuoteService"/> <!-- inject literal values --> <property name="email" value="ranjita@spring.com"/> <property name="name" value="ranjita das"/> </bean>
And a private variable to store those values:
1
2
3
4
5
6
7
8
9
10
private String email;
private String name;
//setter method
public void setEmail(String email) {
this.email = email;
}
public void setName(String name) {
this.name = name;
}
The only problem with this approach is that we have hardcoded values. To solve this we can create a properties file to store our values and use them in the config file.
Make a details.properties file.
1
2
det.email = ranjita @spring.com
det.name = ranjita das
To use them in config file:
1 2 3 4 5 6 7 8 9 10 11 12 13
<context:property-placeholder location="classpath:details.properties"/> < bean id = "myPhysicsTeacher" class = "com.nikhil.IoC.PhysicsTeacher" > <!-- set-up setter injection--> <property name="quoteService" ref="myQuoteService"/> < !--inject literal values-- > <property name="email" value="{det.email}"/> < property name = "name" value = "{det.name}" / > </bean>