Before we dive into dependency injection you must learn about the concept of inversion of control using Java annotations, which we can find in this article. It also contains the setup for the project which we will use in this article, so please have a look.
Dependency Injection (DI) allows a program design to follow the dependency inversion principle. The client delegates the responsibility of providing its dependencies to calls to another object. 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.
AutoWiring for dependency injection Spring can automatically wire up your objects together. Spring looks for a class that fulfills the property and it matches the type either to class or interface. Once Spring finds a match it will automatically inject it, hence it is called autowired.
Steps for injecting QuoteService dependency:
Spring scans for @Component annotation
Find if any class implements the QuoteService interface
If so, inject it. For example, MotivationalQuoteService.
AutoWiring can be done using three methods:
Constructor injection
Setter injection
Field injection
We will discuss all three of them in this article and code through them.
To use constructor injection, we must follow three steps:
Create and define the dependency interface and class
Create a constructor in class for injections
Configure the dependency injection with @Autowired annotation.
Create and define the dependency interface and class
Create one interface and class named QuoteService and MotivationalQuoteService respectively. We also want to add @Component annotation so Spring remembers to scan our package and register our component, we want our MotivationalQuoteService to be registered. Add one method to the interface getQuote() which then is implemented by the class.
QuoteService.java
1
2
3
4
5
6
7
package com.nikhil.anno_demo;
public interface QuoteService {
public String getQuote();
}
MotivationalQuoteService.java
1
2
3
4
5
6
7
8
9
10
11
12
package com.nikhil.anno_demo;
import org.springframework.stereotype.Component;
@Component
public class MotivationalQuoteService implements QuoteService {
@Override
public String getQuote() {
return "kl kare se aj kr aj kre so ab";
}
}
Now, let’s add one method to our Coach interface (which we created in our previous article), getDailyQuote(). As we know the ChemistryTeacher implements this interface, we have to provide the implementation, and here we also have to move on to our next steps.
Create a constructor in your class for injections and configure the dependency injection with @Autowired annotation.
Now in the ChemistryTeacher class, we make a private variable quoteService and initialize quoteService using the constructor of the class named as per the class name.
Now to configure dependency injection using Autowired annotation, initialize the constructor using this @Autowired annotation.
Chemistryteacher.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 com.nikhil.anno_demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ChemistryTeacher implements Teacher {
private QuoteService quoteService;
@Autowired
public ChemistryTeacher(QuoteService quoteService) {
this.quoteService = quoteService;
}
@Override
public String getDailyHomework() {
return "Revise thermodyamics";
}
@Override
public String getDailyQuote() {
return quoteService.getQuote();
}
}
Doing this, Spring will scan for a component that implements QuoteService interface, in our project that is MotivationalQuoteService, which meets the requirements.
Now to see everything in action, create a demo class and do the following steps:
Read Spring from the config file
Get the bean from the Spring container
Call method to get the daily quote
Close the context
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.nikhil.anno_demo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationDemoApp {
public static void main(String[] args) {
// read spring config file
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// get the bean from the spring container
Teacher chem = context.getBean("chemistryTeacher", Teacher.class);
// call method
System.out.println(chem.getDailyHomework());
// call method to get the daily quote
System.out.println(chem.getDailyQuote());
// close context
context.close();
}
}
Console:
Looks like everything went according to the plan.
In setter injection we basically inject dependencies by calling setter methods on the class. We will continue our work on our current example to get a better grasp of this concept.
We are going to inject a MotivationalQuoteService into our teacher implementation and Spring is going to scan all components to see if there is any one that implements the QuoteService. If so, it will inject it.
To use setter injection, we must follow two steps:
Create setter methods in your class for injections
Configure the dependency injection with @Autowired annotation
In our chemistry teacher class by now we have a constructor for the injection. Now we go ahead and define a setter to initialize our QuoteService.
1
2
3
public void setQuoteService(QuoteService quoteService) {
this.quoteService = quoteService;
}
Configure the dependency injection with @Autowired annotation
This step is easy as well; go ahead right on the setter method and annotate it with the Autowired keyword to configure the dependency injection.
ChemistryTeacher.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
package com.nikhil.anno_demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ChemistryTeacher implements Teacher {
private QuoteService quoteService;
@Autowired
public void setQuoteService(QuoteService quoteService) {
System.out.println("Chemistry teacher: in setter");
this.quoteService = quoteService;
}
@Override
public String getDailyHomework() {
return "Revise thermodyamics";
}
@Override
public String getDailyQuote() {
return quoteService.getQuote();
}
}
We did the above to test it. Go ahead and run the same demo class again, you should see the same results as above.
Note: We can use any method to inject dependencies in our classes, as you can see above that constructors and setters are themselves some functions.
Using field injection we can inject dependencies by setting field values on your classes directly, even on private fields, with the help of Java technology called Reflection.
To use field injection, we have to configure the dependency injection with autowired annotation, with no need for setter methods.
You can directly attach the autowired annotation to the field like below.
@Autowired
private QuoteService quoteService;
Here Spring creates your object and automatically sets this field behind the scenes so we won’t have to go through any setter methods using the reflection technology.
Now to test it just go to your ChemistryTeacher class, remove the setter method, and add the above lines.
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.anno_demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ChemistryTeacher implements Teacher {
@Autowired
private QuoteService quoteService;
public ChemistryTeacher() {
System.out.println(">> Inside the default contructor");
}
@Override
public String getDailyHomework() {
return "Revise thermodyamics";
}
@Override
public String getDailyQuote() {
return quoteService.getQuote();
}
}
Here the first thing Spring does is construct the class by calling the default constructor and then it will actually inject a quoteService implementation directly into this class, making use of reflection.
Now run the demo class to test our code:
Woolaa!! Everything works.
Which is best to go with among the three: Constructor, Setter, or Field?
You must choose one method and stay consistent within your project. If you find yourself working in a team, it is necessary for these scenarios that it’s one consistent approach throughout. All three provide the same functionality regardless of the injection type used.
What if there are multiple QuoteService interfaces?
This is a genuine problem. If you have multiple implementations then while using the bean you will get an exception (NoUniqueBeanDefinitionException). In order to resolve this you have to provide Spring a unique bean. You can use a special annotation @Qualifier and give a bean ID like
As we discussed above, to use the default name we lowercase the first letter only.